首页 > 其他分享 >SpringBoot的分层解耦(三层架构,控制反转,依赖注入)

SpringBoot的分层解耦(三层架构,控制反转,依赖注入)

时间:2024-07-28 20:59:38浏览次数:10  
标签:map 架构 SpringBoot person public Person 分层 IOC id

目录

一、项目结构

二、案例引入

三、三层架构

1. 介绍

2. 代码拆分

(1)控制层 Controller

(2)业务逻辑层 Service

业务接口

业务实现类

(3)数据访问层 Dao

数据访问接口

数据访问实现类

(4)接口测试

四、解除耦合

1. 高内聚与低耦合

2. 控制反转和依赖注入

3. 解耦后的代码

(1)控制层 Controller

(2)业务逻辑层 Service

业务接口

业务实现类

(3)数据访问层 Dao

数据访问接口

数据访问实现类

4. IOC和DI的进一步探讨

(1) IOC

@Controller

@Service

@Repository 

(2) DI

@Primary

@Qualifier

@Resource


一、项目结构

Person类:含有id,age,name属性。

二、案例引入

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

对于一个根据ID查询第二年的Person信息的功能,如果不采用分层的Controller代码如下:

@RestController
public class PersonController {

    //接收请求
    @RequestMapping("/getListById")
    public Result<Person> findPerson(@RequestParam(value = "id") Integer id) {

        // 模拟从数据库中读取数据,并返回查找到的Person
        Map<Integer, Person> map = readFromDataBase();
        Person person = map.get(id);

        // 处理业务逻辑
        person.setAge(person.getAge() + 1);

        // 返回数据
        return Result.success(person);
    }

    private Map<Integer, Person> readFromDataBase() {
        Map<Integer, Person> map = new HashMap<>();
        map.put(1, new Person(1, 23, "张三"));
        map.put(2, new Person(2, 24, "李四"));
        return map;
    }

}

可以看出,如果不采用分层设计的做法,将接收参数、读取数据、处理业务逻辑以及返回结果全部封装在一个函数内,随着项目时间的推移和功能模块的增多,这样的函数会变得越来越庞大和复杂。

这种方式会带来诸多问题,如代码的可维护性降低、扩展性受限、复用性不足。因此有必要借助分层思想来优化这种设计模式。换言之,分层的优点有:复用性强、便于维护、利于扩展

三、三层架构

1. 介绍

其实上述案例的处理逻辑从组成上看可以分为三个部分:

  • 数据访问:负责业务数据的维护操作,包括增、删、改、查等操作。

  • 逻辑处理:负责业务逻辑处理的代码。

  • 请求处理、响应数据:负责,接收页面的请求,给页面响应数据。

按照上述的三个组成部分,在项目开发中可以将代码分为三层:

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

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

  • Dao:数据访问层(Data Access Object),也称为持久层。负责数据访问操作,包括数据的增、删、改、查。

2. 代码拆分

(1)控制层 Controller

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

@RestController
public class PersonController {
    //接收请求
    @RequestMapping("/getListById")
    public Result<Person> findPerson(@RequestParam(value = "id") Integer id) {
        //调用Service层方法
        PersonService personService = new PersonServiceImpl();
        Person person = personService.findPerson(id);

        // 返回数据
        return Result.success(person);
    }
}

(2)业务逻辑层 Service

处理具体的业务逻辑。

业务接口
public interface PersonService {
    Person findPerson(Integer id);
}
业务实现类
public class PersonServiceImpl implements PersonService {
    @Override
    public Person findPerson(Integer id) {
        //调用Dao方法
        PersonMapper personMapper = new PersonMapperImpl();
        Person person = personMapper.getListById(id);

        //处理业务逻辑
        person.setAge(person.getAge() + 1);
        return person;
    }
}

(3)数据访问层 Dao

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

数据访问接口
public interface PersonMapper {
    Person getListById(Integer id);
}
数据访问实现类
public class PersonMapperImpl implements PersonMapper {
    // 构造数据用来模拟数据库
    private Map<Integer, Person> readFromDataBase() {
        Map<Integer, Person> map = new HashMap<>();
        map.put(1, new Person(1, 23, "张三"));
        map.put(2, new Person(2, 24, "李四"));
        return map;
    }

    @Override
    public Person getListById(Integer id) {
        // 模拟从数据库中读取数据,并返回查找到的person
        Map<Integer, Person> map = readFromDataBase();
        Person person = map.get(id);
        return person;
    }
}

(4)接口测试

四、解除耦合

1. 高内聚与低耦合

软件开发涉及到的两个概念:内聚和耦合。

  • 内聚:软件中各个功能模块内部的功能联系。

  • 耦合:衡量软件中各个层/模块之间的依赖、关联的程度。

软件设计原则:高内聚低耦合。

高内聚:一个模块中各个元素之间的联系的紧密程度,如果各个元素(语句、程序段)之间的联系程度越高,则内聚性越高,即“高内聚”。

低耦合:软件中各个层、模块之间的依赖关联程序越低越好。

高内聚、低耦合的目的是使程序模块的可重用性、移植性大大增强。

需要什么对象,就直接new一个就可以了, 这种做法层与层之间代码就耦合了。因此不使用new创建对象。

但是不通过new创建对象,程序就会报错,因此可以提供一个解决思路:提供一个容器,把对象放到容器中存储,当程序运行时,就从容器中拿出对象以供程序正常运行

2. 控制反转和依赖注入

想要实现上述解决思路,就涉及到Spring中的两个核心概念:

  • 控制反转: Inversion Of Control,简称IOC。对象的创建控制权由程序自身转移到外部容器,这种思想称为控制反转。

    对象的创建权由程序员主动创建转移到容器(由容器创建、管理对象)。这个容器称为:IOC容器或Spring容器

  • 依赖注入: Dependency Injection,简称DI。容器为应用程序提供运行时所依赖的资源,称之为依赖注入。

    程序运行时需要某个资源,此时容器就为其提供这个资源。

    例:Controller程序运行时需要Service对象,Spring容器就为其提供并注入Service对象。

IOC容器中创建、管理的对象,称之为:bean对象。

模型如下所示:

以Controller获取Service对象为例,具体模型如下所示:

3. 解耦后的代码

(1)控制层 Controller

@RestController
public class PersonController {

    @Autowired //运行时,从IOC容器中获取该类型对象,赋值给该变量
    private PersonService personService;

    //接收请求
    @RequestMapping("/getListById")
    public Result<Person> findPerson(@RequestParam(value = "id") Integer id) {
        //调用Service层方法
        Person person = personService.findPerson(id);

        // 返回数据
        return Result.success(person);
    }

}

(2)业务逻辑层 Service

处理具体的业务逻辑。

业务接口
public interface PersonService {
    Person findPerson(Integer id);
}
业务实现类
@Component //将当前对象交给IOC容器管理,成为IOC容器的bean
public class PersonServiceImpl implements PersonService {

    @Autowired //运行时,从IOC容器中获取该类型对象,赋值给该变量
    private PersonMapper personMapper; 

    @Override
    public Person findPerson(Integer id) {
        Person person = personMapper.getListById(id);
        person.setAge(person.getAge() + 1);
        return person;
    }

}

(3)数据访问层 Dao

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

数据访问接口
public interface PersonMapper {
    Person getListById(Integer id);
}
数据访问实现类
@Component //将当前对象交给IOC容器管理,成为IOC容器的bean
public class PersonMapperImpl implements PersonMapper {

    // 构造数据用来模拟数据库
    private Map<Integer, Person> readFromDataBase() {
        Map<Integer, Person> map = new HashMap<>();
        map.put(1, new Person(1, 23, "张三"));
        map.put(2, new Person(2, 24, "李四"));
        return map;
    }

    @Override
    public Person getListById(Integer id) {
        // 模拟从数据库中读取数据,并返回查找到的person
        Map<Integer, Person> map = readFromDataBase();
        Person person = map.get(id);
        return person;
    }

}

4. IOC和DI的进一步探讨

(1) IOC

在之前的入门案例中,要把某个对象交给IOC容器管理,需要在类上添加一个注解:@Component

而Spring框架为了更好的标识web应用程序开发当中,bean对象到底归属于哪一层,又提供了@Component的衍生注解:

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

@Controller
@RestController // @RestController = @Controller + @ResponseBody
public class PersonController {

    @Autowired //运行时,从IOC容器中获取该类型对象,赋值给该变量
    private PersonService personService;

    //接收请求
    @RequestMapping("/getListById")
    public Result<Person> findPerson(@RequestParam(value = "id") Integer id) {
        //调用Service层方法
        Person person = personService.findPerson(id);

        // 返回数据
        return Result.success(person);
    }

}
@Service
@Service //将当前对象交给IOC容器管理,成为IOC容器的bean
public class PersonServiceImpl implements PersonService {

    @Autowired
    private PersonMapper personMapper;

    @Override
    public Person findPerson(Integer id) {
        Person person = personMapper.getListById(id);
        person.setAge(person.getAge() + 1);
        return person;
    }

}
@Repository 
@Repository //将当前对象交给IOC容器管理,成为IOC容器的bean
public class PersonMapperImpl implements PersonMapper {

    // 构造数据用来模拟数据库
    private Map<Integer, Person> readFromDataBase() {
        Map<Integer, Person> map = new HashMap<>();
        map.put(1, new Person(1, 23, "张三"));
        map.put(2, new Person(2, 24, "李四"));
        return map;
    }

    @Override
    public Person getListById(Integer id) {
        // 模拟从数据库中读取数据,并返回查找到的person
        Map<Integer, Person> map = readFromDataBase();
        Person person = map.get(id);
        return person;
    }

}

(2) DI

在上面的案例中,使用了@Autowired这个注解,完成了依赖注入的操作,而这个Autowired翻译过来叫:自动装配。

@Autowired注解,默认是按照类型进行自动装配的(去IOC容器中找某个类型的对象,然后完成注入操作)

如果该类型有多个:

则会抛出异常。

***************************
APPLICATION FAILED TO START
***************************

Description:

Field personMapper in com.example.service.Impl.PersonServiceImpl required a single bean, but 2 were found:
	- personMapperImpl: defined in file [C:\Users\18039\Desktop\SpringBootTest\springbootdemo\target\classes\com\example\mapper\Impl\PersonMapperImpl.class]
	- personMapperImpl2: defined in file [C:\Users\18039\Desktop\SpringBootTest\springbootdemo\target\classes\com\example\mapper\Impl\PersonMapperImpl2.class]


Action:

Consider marking one of the beans as @Primary, updating the consumer to accept multiple beans, or using @Qualifier to identify the bean that should be consumed

如何解决上述问题呢?Spring提供了以下几种解决方案:

  • @Primary

  • @Qualifier

  • @Resource

@Primary

@Qualifier

@Resource

@Autowird 与 @Resource的区别

  • @Autowired 是spring框架提供的注解,而@Resource是JDK提供的注解

  • @Autowired 默认是按照类型注入,而@Resource是按照名称注入

标签:map,架构,SpringBoot,person,public,Person,分层,IOC,id
From: https://blog.csdn.net/m0_53140426/article/details/140727157

相关文章

  • 计算机毕业设计-基于Java+SSM架构的校园美食交流系统项目开发实战(附源码+论文)
    大家好!我是程序员一帆,感谢您阅读本文,欢迎一键三连哦。......
  • springboot项目嵌入式数据库驱动程序配置及使用方法
    自用文章,仅做参考。目录自用文章,仅做参考。项目创建依赖导入配置文件至此,数据库连接完成。基本用法数据库数据准备1.插入一行2.查询单行多列3.查询多行多列至此,关于springboot中使用嵌入式数据库的方法介绍完成。项目创建选择SQL中的JDBCAPI选型依赖导入......
  • SpringBoot应用零停机滚动更新
    目录1SpringBoot零停机滚动更新1.1引言1.2单体应用设计思路1.3单体应用实现代码1SpringBoot零停机滚动更新1.1引言在个人或者企业服务器上,总归有要更新代码的时候,普通的做法必须先终止原来进程,因为新进程和老进程端口是一个,新进程在启动时候,必定会出现端口占用的情况,但是......
  • 【AI+技术】日志分析:分层告警
    ❀威胁感知层威胁感知层是网络安全体系结构中的一部分,旨在实时监测、分析和感知网络中的安全威胁和异常活动。以下是威胁感知层中的几个重要组成部分的详细介绍:安全检测安全检测是威胁感知层的核心组成部分,负责通过监控网络流量、系统日志、主机行为等方式,实时检测和识别潜......
  • 实战: SpringBoot中5种增强的方法 : 加解密、脱敏、格式转换、时间时区处理(码到三十五)
    1.使用@JsonSerialize和@JsonDeserialize注解2.全局配置Jackson的ObjectMapper3.使用@ControllerAdvice配合@InitBinder4. 自定义HttpMessageConverter5.使用AOP进行切面编程结语在SpringBoot中,对接口的请求入参和出参进行自定义的增强或者修改,通常有以下......
  • SpringBoot 依赖之Validation
    ValidationValidation依赖名称:Validation功能描述:BeanValidationwithHibernatevalidator.使用Hibernate验证器进行Bean验证。<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-valid......
  • 简易版:在 SpringBoot 中设计一个订单号生成系统
     ​博客主页:   南来_北往系列专栏:SpringBoot实战引言要在SpringBoot中设计一个订单号生成系统,你可以按照以下步骤进行:创建一个SpringBoot项目,添加必要的依赖,如spring-boot-starter-web。创建一个订单号生成器类,实现订单号的生成逻辑。可以使用时间戳、随机数......
  • 微信小程序图书馆座位预约管理系统(SpringBoot后端+Vue管理端)附项目源码与配套文档
    目的和意义微信小程序图书馆座位预约管理系统可以对微信小程序图书馆座位预约管理系统信息进行集中管理,可以真正避免传统管理的缺陷。微信小程序图书馆座位预约管理系统是一款运用软件开发技术设计实现的应用系统,在信息处理上可以达到快速的目的,不管是针对数据添加,数据维护和......
  • SpringBoot+Vue电影院售票系统 - 附源码与配套文档
    1.1 研究背景随着互联网技术的迅速发展和普及,人们的生活方式发生了深刻变革,对于文化娱乐消费的需求日益增长,而作为文化消费重要组成部分的电影行业也迎来了前所未有的发展机遇。然而,传统的电影院售票模式,如现场购票、电话预定等,已难以满足现代消费者对便捷性、灵活性和个性......
  • 毕业设计:基于Springboot的在线小说阅读平台【代码+论文+PPT】
    全文内容包括:1、采用技术;2、系统功能;3、系统截图;4、配套内容。索取方式见文末微信号,欢迎关注收藏!一、采用技术语言:Java1.8框架:SpringBoot数据库:MySQL5.7、8.0开发工具:IntelliJIDEA旗舰版其他:Maven3.8以上二、系统功能会员管理:负责用户注册、登录、会员等级划分及用户信......