Java分层架构

Posted FairyKunKun

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java分层架构相关的知识,希望对你有一定的参考价值。

总领:是企业开发中的一种模式、公式、套路。

现实生活中的分层:

大学为例

程序世界中同样也采用了分层:

比如:

王者荣耀,注册用户时,要求昵称必须唯一。

1. 出现填写昵称的界面

                          ------都在用户界面上操作

2. 填写昵称,点击确定

3. 确定完成后

  如果昵称是唯一的,给你注册

                                                ----业务逻辑的判断

  如果昵称不唯一,提示错误信息“昵称已经存在!”

对于使用者而言,认为王者荣耀有一个注册的功能,程序中又称为一个注册业务。

业务:软件中的功能,有时也连起来说叫业务功能。

除了注册业务功能外,还有其他的业务功能,如:登录、抽奖、兑换、对战、积分、升级。。。。

这些都是业务功能。

4. 需要使用到数据访问对象,才能完成整体的业务。   -----------纯的数据访问

根据昵称查询数据库中是否已经存在用户了

如果不存在,就会在数据库中保存一条数据

如果存在,不保存。

分为三个独立的层

分层具体在程序中如何体现?

使用的是包结构来体现

三个核心的包,uiservicedao包   +  辅助包,如: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分层架构的主要内容,如果未能解决你的问题,请参考以下文章

工作总结:mvc分层架构

Java EE 中的分层架构

Java分层架构

在 Java 中实施分层架构

转载常见软件架构分类

连接mysq数据库,创建用户模型