通俗理解JavaWeb三层架构

前言

JavaWeb开发中经常说的Controller层、Service层、Dao层这三层架构,这三层分别做了什么事情,这么分又有什么意义呢?接下来我们用一个简单的案例来详细讲解。

案例

  1. 需求说明
    加载并解析xml文件中的数据,完成数据处理,并在页面展示
  2. 准备工作
    (1) xml数据
    <?xml version="1.0" encoding="UTF-8" ?>
    <emps>
        <emp>
            <name>金毛狮王</name>
            <age>55</age>
            <image>https://web-framework.oss-cn-hangzhou.aliyuncs.com/web/1.jpg</image>
            <!-- 1: 男, 2: 女 -->
            <gender>1</gender>
            <!-- 1: 讲师, 2: 班主任 , 3: 就业指导 -->
            <job>1</job>
        </emp>
    
        <emp>
            <name>白眉鹰王</name>
            <age>65</age>
            <image>https://web-framework.oss-cn-hangzhou.aliyuncs.com/web/2.jpg</image>
            <gender>1</gender>
            <job>1</job>
        </emp>
    
        <emp>
            <name>青翼蝠王</name>
            <age>45</age>
            <image>https://web-framework.oss-cn-hangzhou.aliyuncs.com/web/3.jpg</image>
            <gender>1</gender>
            <job>2</job>
        </emp>
    
        <emp>
            <name>紫衫龙王</name>
            <age>38</age>
            <image>https://web-framework.oss-cn-hangzhou.aliyuncs.com/web/4.jpg</image>
            <gender>2</gender>
            <job>3</job>
        </emp>
    </emps>
    
    
    放在 src/main/resources目录下
    在这里插入图片描述
    (2)解析xml工具类
    package org.example.springbootwebquickstart.utils;
    
    import org.dom4j.Document;
    import org.dom4j.Element;
    import org.dom4j.io.SAXReader;
    import java.io.File;
    import java.lang.reflect.Constructor;
    import java.util.ArrayList;
    import java.util.List;
    
    public class XmlParserUtils {
    
        public static <T> List<T> parse(String file , Class<T> targetClass)  {
            ArrayList<T> list = new ArrayList<T>(); //封装解析出来的数据
            try {
                //1.获取一个解析器对象
                SAXReader saxReader = new SAXReader();
                //2.利用解析器把xml文件加载到内存中,并返回一个文档对象
                Document document = saxReader.read(new File(file));
                //3.获取到根标签
                Element rootElement = document.getRootElement();
                //4.通过根标签来获取 user 标签
                List<Element> elements = rootElement.elements("emp");
    
                //5.遍历集合,得到每一个 user 标签
                for (Element element : elements) {
                    //获取 name 属性
                    String name = element.element("name").getText();
                    //获取 age 属性
                    String age = element.element("age").getText();
                    //获取 image 属性
                    String image = element.element("image").getText();
                    //获取 gender 属性
                    String gender = element.element("gender").getText();
                    //获取 job 属性
                    String job = element.element("job").getText();
    
                    //组装数据
                    Constructor<T> constructor = targetClass.getDeclaredConstructor(String.class, Integer.class, String.class, String.class, String.class);
                    constructor.setAccessible(true);
                    T object = constructor.newInstance(name, Integer.parseInt(age), image, gender, job);
    
                    list.add(object);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            return list;
        }
    
    }
    
    

(3)前端页面资源
在这里插入图片描述
在SpringBoot项目中,静态资源默认可以存放的目录:

  • classpath:/static/
  • classpath:/public/
  • classpath:/resources/
  • classpath:/META-INF/resources/

classpath:

  • 代表的是类路径,在maven的项目中,其实指的就是 src/main/resources 或者 src/main/java,但是java目录是存放java代码的,所以相关的配置文件及静态资源文档,就放在 src/main/resources下。
  1. 实现步骤
    (1)在pom.xml文件中引入dom4j的依赖,用于解析XML文件
    <dependency>
        <groupId>org.dom4j</groupId>
        <artifactId>dom4j</artifactId>
        <version>2.1.3</version>
    </dependency>
    

(2)创建解析XML的工具类XMLParserUtils、实体类Emp、XML文件emp.xml
在这里插入图片描述
(3)静态页面文件,放在resources下的static目录下
在这里插入图片描述
(4)创建EmpController类,编写Controller程序,处理请求,响应数据
在这里插入图片描述
4. 代码实现
Contriller代码:

@RestController
public class EmpController {
    @RequestMapping("/listEmp")
    public Result list(){
        //1. 加载并解析emp.xml
        String file = this.getClass().getClassLoader().getResource("emp.xml").getFile();
        //System.out.println(file);
        List<Emp> empList = XmlParserUtils.parse(file, Emp.class);

        //2. 对数据进行转换处理 - gender, job
        empList.stream().forEach(emp -> {
            //处理 gender 1: 男, 2: 女
            String gender = emp.getGender();
            if("1".equals(gender)){
                emp.setGender("男");
            }else if("2".equals(gender)){
                emp.setGender("女");
            }

            //处理job - 1: 讲师, 2: 班主任 , 3: 就业指导
            String job = emp.getJob();
            if("1".equals(job)){
                emp.setJob("讲师");
            }else if("2".equals(job)){
                emp.setJob("班主任");
            }else if("3".equals(job)){
                emp.setJob("就业指导");
            }
        });
        //3. 响应数据
        return Result.success(empList);
    }
}

统一返回结果实体类:

public class Result {
    private Integer code ;//1 成功 , 0 失败
    private String msg; //提示信息
    private Object data; //数据 date

    public Result() {
    }
    public Result(Integer code, String msg, Object data) {
        this.code = code;
        this.msg = msg;
        this.data = data;
    }
    public Integer getCode() {
        return code;
    }
    public void setCode(Integer code) {
        this.code = code;
    }
    public String getMsg() {
        return msg;
    }
    public void setMsg(String msg) {
        this.msg = msg;
    }
    public Object getData() {
        return data;
    }
    public void setData(Object data) {
        this.data = data;
    }
    public static Result success(Object data){
        return new Result(1, "success", data);
    }
    public static Result success(){
        return new Result(1, "success", null);
    }
    public static Result error(String msg){
        return new Result(0, msg, null);
    }
}
  1. 测试
    代码编写完毕之后,我们就可以运行引导类,启动服务进行测试了。

使用Postman测试:
在这里插入图片描述
打开浏览器,在浏览器地址栏输入: http://localhost:8080/emp.html
在这里插入图片描述

问题分析

上述案例的功能,我们虽然已经实现,但是呢,我们会发现案例中:解析XML数据,获取数据的代码,处理数据的逻辑的代码,给页面响应的代码全部都堆积在一起了,全部都写在controller方法中了。

在这里插入图片描述
当前程序的这个业务逻辑还是比较简单的,如果业务逻辑再稍微复杂一点,我们会看到Controller方法的代码量就很大了。

  • 当我们要修改操作数据部分的代码,需要改动Controller

  • 当我们要完善逻辑处理部分的代码,需要改动Controller

  • 当我们需要修改数据响应的代码,还是需要改动Controller

这样呢,就会造成我们整个工程代码的复用性比较差,而且代码难以维护。 那如何解决这个问题呢?其实在现在的开发中,有非常成熟的解决思路,那就是分层开发。

分层解耦

三层架构
  1. 介绍
    在我们进行程序设计以及程序开发时,尽可能让每一个接口、类、方法的职责更单一些(单一职责原则)。

单一职责原则:一个类或一个方法,就只做一件事情,只管一块功能。

这样就可以让类、接口、方法的复杂度更低,可读性更强,扩展性更好,也更利用后期的维护。

我们之前开发的程序呢,并不满足单一职责原则。下面我们来分析下之前的程序:
在这里插入图片描述
那其实我们上述案例的处理逻辑呢,从组成上看可以分为三个部分:

  • 数据访问:负责业务数据的维护操作,包括增、删、改、查等操作。
  • 逻辑处理:负责业务逻辑处理的代码。
  • 请求处理、响应数据:负责,接收页面的请求,给页面响应数据。

按照上述的三个组成部分,在我们项目开发中呢,可以将代码分为三层:
在这里插入图片描述

  • Controller:控制层。接收前端发送的请求,对请求进行处理,并响应数据。
  • Service:业务逻辑层。处理具体的业务逻辑。
  • Dao:数据访问层(Data Access Object),也称为持久层。负责数据访问操作,包括数据的增、删、改、查。

基于三层架构的程序执行流程:
在这里插入图片描述

  • 前端发起的请求,由Controller层接收(Controller响应数据给前端)
  • Controller层调用Service层来进行逻辑处理(Service层处理完后,把处理结果返回给Controller层)
  • Serivce层调用Dao层(逻辑处理过程中需要用到的一些数据要从Dao层获取)
  • Dao层操作文件中的数据(Dao拿到的数据会返回给Service层)

思考:按照三层架构的思想,如何要对业务逻辑(Service层)进行变更,会影响到Controller层和Dao层吗?
答案:不会影响。 (程序的扩展性、维护性变得更好了)

  1. 代码拆分
    我们使用三层架构思想,来改造下之前的程序:
  • 控制层包名:xxxx.controller
  • 业务逻辑层包名:xxxx.service
  • 数据访问层包名:xxxx.dao

控制层:接收前端发送的请求,对请求进行处理,并响应数据

@RestController
public class EmpController {
    //业务层对象
    private EmpService empService = new EmpServiceA();

    @RequestMapping("/listEmp")
    public Result list(){
        //1. 调用service层, 获取数据
        List<Emp> empList = empService.listEmp();

        //3. 响应数据
        return Result.success(empList);
    }
}

业务逻辑层:处理具体的业务逻辑

  • 业务接口
//业务逻辑接口(制定业务标准)
public interface EmpService {
    //获取员工列表
    public List<Emp> listEmp();
}
  • 业务实现类
//业务逻辑实现类(按照业务标准实现)
public class EmpServiceA implements EmpService {
    //dao层对象
    private EmpDao empDao = new EmpDaoA();

    @Override
    public List<Emp> listEmp() {
        //1. 调用dao, 获取数据
        List<Emp> empList = empDao.listEmp();

        //2. 对数据进行转换处理 - gender, job
        empList.stream().forEach(emp -> {
            //处理 gender 1: 男, 2: 女
            String gender = emp.getGender();
            if("1".equals(gender)){
                emp.setGender("男");
            }else if("2".equals(gender)){
                emp.setGender("女");
            }

            //处理job - 1: 讲师, 2: 班主任 , 3: 就业指导
            String job = emp.getJob();
            if("1".equals(job)){
                emp.setJob("讲师");
            }else if("2".equals(job)){
                emp.setJob("班主任");
            }else if("3".equals(job)){
                emp.setJob("就业指导");
            }
        });
        return empList;
    }
}

数据访问层:负责数据的访问操作,包含数据的增、删、改、查

  • 数据访问接口
//数据访问层接口(制定标准)
public interface EmpDao {
    //获取员工列表数据
    public List<Emp> listEmp();
}
  • 数据访问实现类
//数据访问实现类
public class EmpDaoA implements EmpDao {
    @Override
    public List<Emp> listEmp() {
        //1. 加载并解析emp.xml
        String file = this.getClass().getClassLoader().getResource("emp.xml").getFile();
        System.out.println(file);
        List<Emp> empList = XmlParserUtils.parse(file, Emp.class);
        return empList;
    }
}

在这里插入图片描述

总结

Java Web应用的三层架构通常指的是表示层(Presentation Layer)、业务逻辑层(Business Logic Layer)和数据访问层(Data Access Layer)。这种架构模式有助于实现高内聚低耦合的设计原则,使得代码更加模块化,易于维护和扩展。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

SunnyRivers

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值