Java分层架构
Posted FairyKunKun
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java分层架构相关的知识,希望对你有一定的参考价值。
总领:是企业开发中的一种模式、公式、套路。
现实生活中的分层:
大学为例
程序世界中同样也采用了分层:
比如:
王者荣耀,注册用户时,要求昵称必须唯一。
1. 出现填写昵称的界面
------都在用户界面上操作
2. 填写昵称,点击确定
3. 确定完成后
如果昵称是唯一的,给你注册
----业务逻辑的判断
如果昵称不唯一,提示错误信息“昵称已经存在!”
对于使用者而言,认为王者荣耀有一个注册的功能,程序中又称为一个注册业务。
业务:软件中的功能,有时也连起来说叫业务功能。
除了注册业务功能外,还有其他的业务功能,如:登录、抽奖、兑换、对战、积分、升级。。。。
这些都是业务功能。
4. 需要使用到数据访问对象,才能完成整体的业务。 -----------纯的数据访问
根据昵称查询数据库中是否已经存在用户了
如果不存在,就会在数据库中保存一条数据
如果存在,不保存。
分为三个独立的层
分层具体在程序中如何体现?
使用的是包结构来体现
三个核心的包,ui、service、dao包 + 辅助包,如:util、exception、test、。。。。
其中service、dao采用的是面向接口的编程方式:更抽象、更好使用多态、更好做替换、程序的可插拔行高、灵活。。。。。。。
又多出了两个包,service.impl 、dao.impl包
需求:
新闻系统,专门发布新闻。
功能:新闻发布功能,要求:标题、内容,不能含有脏话、不能含有反共的。。。
1. 创建合理的包结构 ----一定要体现分层
现有公司域名+项目名称倒着写
核心的包
其他的辅助包
2. 之前封装的工具拷贝到util包中
3. 具体开发新闻发布功能
可以从上层往下写,也可以从下层往上写,建议从下往上写。
建库、建表
create table t_news (
t_id int auto_increment primary key,
t_title varchar(100),
t_content varchar(1024),
t_createtime datetime
) engine = Innodb;
定义一个实体类News
package com.njwbhz.newssys.entity;
import march.part0320.util.DateUtil;
import java.util.Date;
/**
* 新闻实体类
*/
public class News
private int id;
private String title;
private String content;
private Date createTime;
public int getId()
return id;
public void setId(int id)
this.id = id;
public String getTitle()
return title;
public void setTitle(String title)
this.title = title;
public String getContent()
return content;
public void setContent(String content)
this.content = content;
public Date getCreateTime()
return createTime;
public void setCreateTime(Date createTime)
this.createTime = createTime;
@Override
public String toString()
return "News" +
"id=" + id +
", title='" + title + '\\'' +
", content='" + content + '\\'' +
", createTime=" + DateUtil.dateToStr(createTime,"yyyy-MM-dd HH:mm:ss") +
'';
定义NewsDao数据访问接口,只体现CURD操作
package com.njwbhz.newssys.dao;
import com.njwbhz.newssys.entity.News;
public interface NewsDao
/**
* 保存一个新闻对象
* @param news
*/
void add(News news);
定义一个NewsDaoImpl
package com.njwbhz.newssys.dao.imple;
import com.njwbhz.newssys.dao.NewsDao;
import com.njwbhz.newssys.entity.News;
import com.njwbhz.newssys.util.JdbcTemplate;
public class NewsDaoImpl implements NewsDao
@Override
public void add(News news)
String sql = "insert into t_news(t_title,t_content,t_createtime) values (?,?,now())";
JdbcTemplate.insert(sql , news.getTitle() , news.getContent());
测试NewsDao
package com.njwbhz.newssys.test;
import com.njwbhz.newssys.dao.NewsDao;
import com.njwbhz.newssys.dao.imple.NewsDaoImpl;
import com.njwbhz.newssys.entity.News;
public class TestNewsDao
public static void main(String[] args)
NewsDao newsDao = new NewsDaoImpl();
News news = new News();
news.setTitle("test_title");
news.setContent("test_content");
newsDao.add(news);
修改db.properties
driverClassName = com.mysql.jdbc.Driver
url = jdbc:mysql://localhost/news?characterEncoding=utf8
username = root
password = root
initialSize = 3
再测试
package com.njwbhz.newssys.exception;
/**
* 自定义的业务异常
*/
public class NewsSysException extends RuntimeException
public NewsSysException (String msg)
super(msg);
新建业务类接口
NewsService
package com.njwbhz.newssys.service;
import com.njwbhz.newssys.entity.News;
import com.njwbhz.newssys.exception.NewsSysException;
public interface NewsService
//定义的方法体现出业务功能
void publish (News news) throws NewsSysException;
新建业务实现类
NewsServiceImpl
package com.njwbhz.newssys.service.impl;
import com.njwbhz.newssys.dao.NewsDao;
import com.njwbhz.newssys.dao.imple.NewsDaoImpl;
import com.njwbhz.newssys.entity.News;
import com.njwbhz.newssys.exception.NewsSysException;
import com.njwbhz.newssys.service.NewsService;
public class NewsServiceImpl implements NewsService
//持有一个NewsDao的引用
private NewsDao newsDao = new NewsDaoImpl();
@Override
public void publish(News news) throws NewsSysException
//业务逻辑
//发布的新闻标题、内容中不准有脏话,反共的话
//模拟
if (news.getTitle().contains("你妹") || news.getContent().contains("你妹")) //含有脏话
//不给你发布
//非正常——>异常
//抛出一个异常
throw new NewsSysException("不能含有脏话");
else //给你发布
//正常
newsDao.add(news);
测试:
package com.njwbhz.newssys.test;
import com.njwbhz.newssys.entity.News;
import com.njwbhz.newssys.exception.NewsSysException;
import com.njwbhz.newssys.service.NewsService;
import com.njwbhz.newssys.service.impl.NewsServiceImpl;
public class TestNewsService
public static void main(String[] args)
NewsService newsService = new NewsServiceImpl();
News news = new News();
news.setTitle("test_title_你妹");
news.setContent("test_content2");
try
newsService.publish(news);
catch (NewsSysException e)
System.out.println(e.getMessage());
按照目前为止,newsDao.add会不会出现异常?
答案:肯定不会。
原因:
JdbcTemplate中所有的方法都没有声明异常,即使内部出现异常了,都被
JdbcTemplate方法内部的catch处理了,不会对外暴露异常。
如何解决?
为了把SQLException放出来,不能再JdbcTemplate中处理,应该直接对外抛出。
修改JdbcTemplate类
package com.njwbhz.newssys.util;
import org.apache.log4j.Logger;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
public class JdbcTemplate
//定义一个日志记录器org.apache.log4j.Logger
private static Logger logger = Logger.getLogger(JdbcTemplate.class);
/**
* 专门处理insert update delete的模板方法
* @param sql
* @param params 参数列表 个数和sql中的占位符一致
*/
public static void executeUpdate(String sql,Object ... params) throws SQLException
logger.info(JdbcLogger.getMessage(sql , params));
//1. 加载驱动 封装好了
//2. 获取连接
Connection conn = null;
conn = JdbcUtil.getConnection();
PreparedStatement pstmt = null;
try
pstmt = conn.prepareStatement(sql);
setParams(pstmt,params);
pstmt.executeUpdate();
finally
JdbcUtil.close(pstmt,conn);
private static void setParams(PreparedStatement preparedStatement,Object ... params)
if (!(null == preparedStatement || params.length == 0)) //有参数
for (int i = 0 ; i < params.length ; i++)
try
preparedStatement.setObject((i + 1),params[i]);
catch (SQLException e)
e.printStackTrace();
/**
* 专门处理select的模板方法
* @param sql
* @param rowMapping
* @param params
* @param <T>
* @return
*/
public static <T>List<T> executeQuery (String sql , RowMapping<T> rowMapping , Object ... params) throws SQLException
logger.info(JdbcLogger.getMessage(sql, params));
List<T> datas = new ArrayList<T>();//返回任何一类的实体对象
//模板化的步骤
//1. 加载驱动
//2. 获取链接
Connection conn = null;
conn = JdbcUtil.getConnection();
//3. 获取SQL执行器
PreparedStatement pstmt = null;
ResultSet rs = null;
try
pstmt = conn.prepareStatement(sql);
//4. 执行SQL
setParams(pstmt , params);
rs = pstmt.executeQuery();
//处理结果
while (rs.next())
//一行数据——>映射成实体对象
//User user = new User() 或Dept dept = new Dept()各种各样
//通用的Object object = new Object(),object没有set方法——>没有办法做映射
T data = rowMapping.mapper(rs);
//实体对象添加到集合中
datas.add(data);
finally
JdbcUtil.close(rs , pstmt , conn);
return datas;
/**
* 查询稽核数据
* @param sql
* @param rowMapping
* @param params
* @param <T>
* @return
*/
public static <T> List<T> selectList(String sql , RowMapping<T> rowMapping , Object ... params) throws SQLException
return executeQuery(sql , rowMapping , params);
/**
* 查询单个
* @param sql
* @param rowMapping
* @param params
* @param <T>
* @return
*/
public static <T> T selectOne(String sql , RowMapping rowMapping , Object ... params) throws SQLException
List<T> datas = selectList(sql , rowMapping , params);
if (datas.size() > 0)
return datas.get(0);
else
return null;
public static void insert(String sql , Object ... params) throws SQLException
executeUpdate(sql , params);
public static void delete(String sql , Object ... params) throws SQLException
executeUpdate(sql , params);
public static void update(String sql , Object ... params) throws SQLException
executeUpdate(sql , params);
其他5个方法,只要在方法的签名上throws SQLException
package com.njwbhz.newssys.dao;
import com.njwbhz.newssys.entity.News;
import java.sql.SQLException;
public interface NewsDao
/**
* 保存一个新闻对象
* @param news
*/
void add(News news) throws SQLException;
package com.njwbhz.newssys.dao.imple;
import com.njwbhz.newssys.dao.NewsDao;
import com.njwbhz.newssys.entity.News;
import com.njwbhz.newssys.util.JdbcTemplate;
import java.sql.SQLException;
public class NewsDaoImpl implements NewsDao
@Override
public void add(News news) throws SQLException
String sql = "insert into t_news(t_title,t_content,t_createtime) values (?,?,now())";
JdbcTemplate.insert(sql , news.getTitle() , news.getContent());
package com.njwbhz.newssys.test;
import com.njwbhz.newssys.dao.NewsDao;
import com.njwbhz.newssys.dao.imple.NewsDaoImpl;
import com.njwbhz.newssys.entity.News;
import java.sql.SQLException;
public class TestNewsDao
public static void main(String[] args)
NewsDao newsDao = new NewsDaoImpl();
News news = new News();
news.setTitle("test_title");
news.setContent("test_content");
try
newsDao.add(news);
catch (SQLException e)
e.printStackTrace();
package com.njwbhz.newssys.service.impl;
import com.njwbhz.newssys.dao.NewsDao;
import com.njwbhz.newssys.dao.imple.NewsDaoImpl;
import com.njwbhz.newssys.entity.News;
import com.njwbhz.newssys.exception.NewsSysException;
import com.njwbhz.newssys.service.NewsService;
import java.sql.SQLException;
public class NewsServiceImpl implements NewsService
//持有一个NewsDao的引用
private NewsDao newsDao = new NewsDaoImpl();
@Override
public void publish(News news) throws NewsSysException
//业务逻辑
//发布的新闻标题、内容中不准有脏话,反共的话
//模拟
if (news.getTitle().contains("你妹") || news.getContent().contains("你妹")) //含有脏话
//不给你发布
//非正常——>异常
//抛出一个异常
throw new NewsSysException("不能含有脏话");
else //给你发布
//正常
try
newsDao.add(news);
catch (SQLException e) //捕获数据库的异常
e.printStackTrace();
//异常的转换
throw new NewsSysException("系统维护中或系统故障");
//如果数据库挂了,或者字段的长度超过了数据库的限制
//都会添加不成功
最后写UI层
package com.njwbhz.newssys.ui;
import com.njwbhz.newssys.entity.News;
import com.njwbhz.newssys.exception.NewsSysException;
import com.njwbhz.newssys.service.NewsService;
import com.njwbhz.newssys.service.impl.NewsServiceImpl;
import java.util.Scanner;
public class PublishNewsUi
private NewsService newsService = new NewsServiceImpl();
public void publish ()
Scanner scanner = new Scanner(System.in);
System.out.println("新闻发布页");
System.out.print("新闻标题:");
String title = scanner.next();
System.out.print("新闻的正文:");
String content = scanner.next();
News news = new News();
news.setTitle(title);
news.setContent(content);
//调用业务层逻辑,千万记住不能调用dao,不能越级
try
newsService.publish(news);
System.out.println("新闻发布成功");
catch (NewsSysException e)
System.out.println("新闻发布失败:原因->" + e.getMessage());
测试界面
package com.njwbhz.newssys.ui;
import java.util.Scanner;
/**
* 新闻应用
*/
public class NewsApp
public static void main(String[] args)
System.out.println("新闻管理系统");
System.out.println("1. 发布新闻");
System.out.print("请输入菜单编号:");
Scanner input = new Scanner(System.in);
int menuNo = input.nextInt();
switch (menuNo)
case 1 :
//发布新闻
//发布界面
PublishNewsUi publishNewsUi = new PublishNewsUi();
publishNewsUi.publish();
break;
java中分层架构写代码
以前的人们写代码,都写在main()方法中,如果出现了错误,就慢慢调试,这样调试会浪费很多时间,而程序员的时间是非常宝贵的。但是当使用分层架构的时候,在系统出现错误的时候就可以清晰明确的知道错误出在哪里,现在的公司中百分百都是使用分层架构的,因为那样可以分离我们程序员的关注,使代码更为简洁。
分层架构的好处
分层架构其实带来了很多好处,首先就是方便维护和分离关注点,这一点前面已经提到了。
便于更改或替换
先说说便于替换或者更新,比如我现在用的数据库是SQL Server数据库,如果我需要将数据库更换为MySQL,在增删改查的语法一致的情况下,我们只需要更改BaseDao工具类的连接字符串,而不用更改其它层的代码。
实现软件之间的解耦
解耦之后,每个部分都可以独立变化。
举个简单的例子,画一个三角形和画一个文字,在DX和OpenGL下面用的是截然不同的方法(不仅仅是函数名不一样)。但是你哪一天你发现,你爱用DX还是OpenGL,丝毫不影响你如何画三角形还是文字,爱画什么画什么,那么你画的东西跟你画的手段就解耦了。这个解耦的具体做法就是在中间插入一层接口(interface)。软件的所有问题都可以通过增加一层interface来解决。你的团队越大,需求变化越快,你越要保证程序员之间的依赖关系越少,你的软件解耦的就越好,interface就越多,关系就越复杂(跟乱不一样),应付变化就越容易,钱赚的就越多,越稳定。
做提高软件组件的重用
在软件开发中,由于不同的环境和功能要求,我们可以通过对以往成熟软件系统的局部修改和重组,保持整体稳定性,以适应新要求。这样的软件称为可重用软件。
分层的架构和解析
代码分层可大致分为:
DAO(数据访问对象):该层分为接口和他的实现类来实现功能,dao子目录只提供对外接口,而他的实现类要放到数据访问层,就是impl层。
DTO(Data Transfer Object,数据传输对象):主要用于远程调用等需要大量传输对象的地方。 比如我们一张表有100个字段,那么对应的PO就有100个属性。 但是我们界面上只要显示10个字段, 客户端用WEB Service来获取数据,没有必要把整个PO对象传递到客户端, 这时我们就可以用只有这10个属性的DTO来传递结果到客户端,这样也不会暴露服务端表结构。到达客户端以后,如果用这个对象来对应界面显示,那此时它的身份就转为VO。
BIZ(业务逻辑层):跟dao层一样只提供接口。
Model(实体层):用于读、取数据。
JavaBean:javaBean在MVC设计模型中是model,又称模型层,在一般的程序中,我们称它为数据层,就是用来设置数据的属性和一些行为,然后我会提供获取属性和设置属性的get/set方法。
Servlet:用于跟JSP页面进行交互。
POJO(Plain Ordinary Java Object,简单无规则的Java对象):传统意义的Java对象。就是说,在一些Object/Relation Mapping工具中,能够做到维护数据库表记录的Persisent Object完全是一个符合Java Bean规范的Java对象,没有增加别的属性和方法。我的理解就是最基本的Java Bean,只有属性字段及setter和getter方法。
"你的气质里,藏着你走过的路,读过的书和爱过的人。"
以上是关于Java分层架构的主要内容,如果未能解决你的问题,请参考以下文章