【spingboot】分层 以及 使用IOC,DI解耦

三层架构

定义

在这里插入图片描述

控制层(Controller)

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

业务逻辑层(Service)

  • 业务逻辑层处理具体的业务逻辑。

数据访问层(DAO - Data Access Object)

  • 数据访问层(持久层)负责数据访问操作,包括数据的增、删、改、查。

Java Web应用程序中的执行顺序

在Java Web应用程序中,请求的处理流程通常遵循以下顺序:

  1. 前端请求

    • 前端发送请求至服务器。
  2. 控制层(Controller)

    • 接收请求:控制层首先接收前端发送的请求。
    • 预处理:可能进行一些初步的数据验证或预处理。
    • 调用业务逻辑:根据请求类型和参数,调用相应的业务逻辑层方法。
  3. 业务逻辑层(Service)

    • 处理业务逻辑:执行具体的业务逻辑操作,如计算、决策、事务管理等。
    • 数据访问需求:如果业务逻辑处理中需要访问数据库,则调用数据访问层的方法。
  4. 数据访问层(DAO - Data Access Object)

    • 数据库交互:执行SQL语句(或者其他获取数据的技术)或使用ORM框架进行数据的增、删、改、查操作。
    • 返回结果:将操作结果返回给业务逻辑层。
  5. 业务逻辑层(Service)

    • 继续处理:根据数据访问层返回的结果,继续执行剩余的业务逻辑。
  6. 控制层(Controller)

    • 响应前端:将业务逻辑层处理的结果封装成响应数据,返回给前端。
  7. 前端响应

    • 前端接收到控制层返回的数据,并据此更新用户界面。

简而言之,执行顺序为:

前端请求控制层(Controller)业务逻辑层(Service)数据访问层(DAO)业务逻辑层(Service)控制层(Controller)前端响应

代码实现

初始代码如下:
这段代码复用性差,难以维护
我们要对下面的代码进行优化(拆分

package com.example.springboot_start.controller;

import com.example.springboot_start.pojo.Emp;
import com.example.springboot_start.pojo.Result;
import com.example.springboot_start.utils.XmlParserUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;


import java.util.List;

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

        empList.stream().forEach(emp -> {
            String gender = emp.getGender();
            if ("1".equals(gender)){
                emp.setGender("男");
            } else if ("2".equals(gender)) {
                emp.setGender("nv");
            }

            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 Result.success(empList);
    }
}

把以上代码分成三层架构:
在这里插入图片描述

然后把三层架构的包给新建出来:
在这里插入图片描述

为了功能的灵活性,采用面向接口的方法

dao包的实现

先弄个接口EmpDao,然后在弄个实现类文件夹impl。
实现类文件夹中再新建一个实现类EmpDaoA
这个A的意思就是之后还可能有其他实现类来实现这个接口
在这里插入图片描述
接口:

package com.example.springboot_start.dao;

import com.example.springboot_start.pojo.Emp;

import java.util.List;

public interface EmpDao {
    public List<Emp> listEmp();
}

实现类:
(实际上就是上面那个图中的“数据访问”那一块代码

package com.example.springboot_start.dao.impl;

import com.example.springboot_start.dao.EmpDao;
import com.example.springboot_start.pojo.Emp;
import com.example.springboot_start.utils.XmlParserUtils;

import java.util.List;

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;
    }
}

service层

在这里插入图片描述
接口:

package com.example.springboot_start.service;

import com.example.springboot_start.pojo.Emp;

import java.util.List;

public interface EmpService {
    //先获取员工列表(从DAO那里
    public List<Emp> listEmp();
}

实现类:

package com.example.springboot_start.service.impl;

import com.example.springboot_start.dao.EmpDao;
import com.example.springboot_start.dao.impl.EmpDaoA;
import com.example.springboot_start.pojo.Emp;
import com.example.springboot_start.service.EmpService;

import java.util.List;

public class EmpServiceA implements EmpService {
    //获取DAO
    private EmpDao empDao = new EmpDaoA();
    @Override
    public List<Emp> listEmp() {
        List<Emp> empList = empDao.listEmp();
        //调用dao来获取数据
        empList.stream().forEach(emp -> {
            String gender = emp.getGender();
            if ("1".equals(gender)){
                emp.setGender("男");
            } else if ("2".equals(gender)) {
                emp.setGender("nv");
            }

            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;
    }
}

注意,在这个实现类中有些不太一样
不能完全复制过来,还必须额外引用DAO中的数据
在这里插入图片描述

controller层

在这里插入图片描述
这个就不必用什么接口或者实现类了
代码:

@RestController
public class EmpController {
    //创建service对象(面向接口
    private EmpService empService = new EmpServiceA();
    @RequestMapping("/listEmp")
    public Result list(){
        //调用service获取数据
        List<Emp> empList = empService.listEmp();
        return Result.success(empList);
    }
}

同理,controller层也必须获取service层中数据
在这里插入图片描述

分层解耦

定义

内聚

  • 定义:内聚是指软件中各个功能模块内部的功能联系。它衡量了一个模块内部各个元素之间相互依赖、相互关联的紧密程度。

耦合

  • 定义:耦合是衡量软件中各个层/模块之间依赖、关联程度的指标。它描述了不同模块之间相互依赖的关系强度。

所以我们刚才进行的拆分就是使代码变得更 高内聚,低耦合 的过程
这也是软件设计过程中非常重要的原则:高内聚,低耦合

以刚才的代码为例
**高内聚:**在员工管理的service中,仅仅存在与员工管理相关的逻辑处理
**低耦合:**尽量降低层与层之间或模块与模块之间的依赖,关联程度

引入IOC & DI

但是刚才咱拆分之后的代码还是存在耦合
比如说:
在这里插入图片描述
上面这个图就是controller层的代码

但是如标红所示

EmpController 类直接依赖于 EmpServiceA 这个具体实现类,而不是依赖于 EmpService 接口。这意味着 EmpControllerEmpServiceA 的实现细节紧密相关。

同时,EmpServiceA 被硬编码为 EmpService 的实现,这限制了 EmpController 的灵活性。如果将来需要更换 EmpService 的实现,必须修改 EmpController 类的代码。

所以我们必须把那块标红的搞掉:
在这里插入图片描述
但这声明了变量但是没赋值,会报错,咋办呢

这个时候我们就要用IOCDI

这个就是对控制反转(IOC)和依赖注入(DI)的图解
IoC是有专门一个容器来创建这些对象,即由Ioc容器来控制对象的创建以及外部资源获取(不只是对象包括比如文件等)。
反转则是由容器来帮忙创建及注入依赖对象:由容器帮我们查找及注入依赖对象,对象只是被动的接受依赖对象,所以是反转,依赖对象的获取被反转了
在这里插入图片描述

概念如下:

控制反转 (Inversion Of Control, 简称 IoC)

  • 定义:控制反转是一种设计原则,指对象的创建控制权由程序自身转移到外部(容器)。

依赖注入 (Dependency Injection, 简称 DI)

  • 定义:依赖注入是实现控制反转的一种方法,容器为应用程序提供运行时所依赖的资源。

Bean对象

  • 定义:在IoC容器中创建、管理的对象称为bean。

代码实现

在这里插入图片描述
@Component:

  • 将当前类交给IOC容器管理,成为IOC容器中的bean

@Autowired

  • 运行时,IOC容器会提供该类型的bean对象,并赋值给该变量——依赖注入

这样就完成解耦操作了~

IOC讲解

注解说明位置
@Component声明bean的基础注解不属于以下三类时,用此注解
@Controller@Component的衍生注解标注在控制器类上
@Service@Component的衍生注解标注在业务类上
@Repository@Component的衍生注解标注在数据访问类上(由于与mybatis整合,用的少)

在实际开发中,建议用下面三个注解

比如说我们刚才的代码(看蓝色字体)
在这里插入图片描述
注意@RestController
是由@Controller@ResponseBody组成的
所以不用额外加@Controller

查看Bean

在图中位置查看bean:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
bean的名字的默认值就是bean所在类的名字(首字母变成小写

更改bean的名字:

就是通过value属性更改

@Respository(value = " daoA ")
//属性名字也可以省略
@Respository(" daoA ")
//这俩都是一样的

DI(依赖注入)详解

存在多个相同类型的bean咋办?

以上代码中,我们注入了EmpDaoA 👇

@Repository
public class EmpDaoA implements EmpDao {

那如果还有一个EmpDaoB也注解了@Repository👇

@Repository
public class EmpDaoB implements EmpDao {

那咋整呢?到底注入谁呢?

在这种情况下运行实际上会报错的
like this 👇
在这里插入图片描述

那咋办呢?

@Primary注解

就是多个注解在上面
如下:

@Primary
@Repository
public class EmpDaoA implements EmpDao {

这样运行起来就没毛病了

@Qualifier

被注入 的地方加入注释
然后在括号里面加入bean的名字
注意不是类的名字哦,是bean的捏(默认是类名首字母小写)

	@Autowired
    @Qualifier("empDaoA")
    private EmpDao empDao;

这样运行起来就没毛病了

Resource

在被注入的位置把@Autowired删掉,然后再加入这个注解

    @Resource(name = "empDaoA")
    private EmpDao empDao;

注意这个是以名字注入的,但是其他的是以类型注入的捏

注解定义
@Autowired是Spring框架提供的注解
@Resource是JDK提供的注解
特点@Autowired@Resource
默认注入方式按照类型注入默认按照名称注入
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值