【JavaWeb | 第四篇】分层解耦

目录

一、SpringBootWeb案例

二、分层解耦

[2.1 三层架构](#2.1 三层架构)

[2.2 分层解耦](#2.2 分层解耦)

[2.3 IOC&DI](#2.3 IOC&DI)

[2.3.1 IOC详解](#2.3.1 IOC详解)

[2.3.2 DI详解](#2.3.2 DI详解)


一、SpringBootWeb案例

现在有一个案例,需求是将user.txt文件中的数据返回给客户端的前端页面。

我们把读取数据、业务逻辑处理、响应数据都写到Controller层当中了。

在实际开发中如果使用这种开发方式,会有以下问题:

  • 复用性差
  • 难以维护
java 复制代码
/**
 * 用户信息Controller
 */
@RestController //@Controller + @ResponseBody -----> 如果返回的是一个对象/集合 --> 转json --> 响应
public class UserController {
    @RequestMapping("/list" )
    public List<User> list(){
        //1. 读取user.txt中的数据
        InputStream in = this.getClass().getClassLoader().getResourceAsStream("user.txt");
        ArrayList<String> lines = IoUtil.readUtf8Lines(in, new ArrayList<>());

        //2. 业务逻辑处理: 解析数据, 封装User对象 --> List<User>
        List<User> userList = lines.stream().map(line -> {
            String[] split = line.split(",");
            return new User(
                    Integer.parseInt(split[0]),
                    split[1],
                    split[2],
                    split[3],
                    Integer.parseInt(split[4]),
                    LocalDateTime.parse(split[5], DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
        }).toList();

        //3. 响应数据
        return userList;
    }
}

二、分层解耦

2.1 三层架构

Spring Boot 应用通常遵循经典的"三层架构"。分层的核心目的是单一职责原则****(Single Responsibility Principle),即每一层只专注于处理特定类型的任务。

  • controller控制层,接收前端发送的请求,对请求进行处理,并响应数据。
  • service业务逻辑层,处理具体的业务逻辑。
  • dao数据访问层,负责数据访问操作,包括数据的增删改查。


案例:对第一章节的SpringBoot项目进行拆分

Dao层

java 复制代码
public class UserDaoImpl implements UserDao {
    @Override
    public List<String> findAll() {
        InputStream in = this.getClass().getClassLoader().getResourceAsStream("user.txt");
        ArrayList<String> lines = IoUtil.readUtf8Lines(in, new ArrayList<>());
        return lines;
    }
}

Service层

java 复制代码
public class UserServiceImpl implements UserService {

    private UserDao userDao = new UserDaoImpl();
    @Override
    public List<User> findAll() {
        List<String> lines = userDao.findAll();
        List<User> userList = lines.stream().map(line -> {
            String[] split = line.split(",");
            return new User(
                    Integer.parseInt(split[0]),
                    split[1],
                    split[2],
                    split[3],
                    Integer.parseInt(split[4]),
                    LocalDateTime.parse(split[5], DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
        }).toList();
        return userList;
    }
}

Controller层

java 复制代码
@RestController //@Controller + @ResponseBody -----> 如果返回的是一个对象/集合 --> 转json --> 响应
public class UserController {

    private UserService userService = new UserServiceImpl();

    @RequestMapping("/list")
    public List<User> list() {
        List<User> userList = userService.findAll();
        return userList;
    }
}

2.2 分层解耦

  • 耦合:衡量软件中各个层/各个模块的依赖关联程度。
  • 内聚:软件中各个功能模块内部的功能联系。
  • 软件设计原则:高内聚,低耦合。

我们在实现UserController来接收Service层传过来处理好的数据时,需要自己在UserController中手动创建一个UserServiceImpl的对象,才能调用业务处理的实现。两个层之间存在着联系,我们就称项目耦合。

2.3 IOC&DI

看了上述例子之后,我们可以清楚的感知到目前虽然实现了三层架构,但是项目仍然存在耦合现象,那么如何解决上述现象呢,Spring框架给我们提供了解决方案:

即使使用了接口,最终系统中总需要有一个地方来实例化具体的对象。Spring Boot 通过其核心的 Spring 容器(IoCContainer) 彻底接管了这一过程。这一方法叫做控制反转。

将容器中的对象分配给另一个层的对象时,Spring 容器会在运行时自动将目标对象分配(注入)给它。这一过程叫做DI(依赖注入)

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

案例:将我们上述分成三层架构的SpringBoot项目进行低耦合的调整。

  1. 将Dao和Service层的实现类交给IOC容器管理
  2. 为Controller和Service注入运行时所依赖的对象

1.将Dao和Service层的实现类,交给IOC容器管理。只需要在Dao、Service的实现类顶部添加一个**@Component**注解。

2.为Controller和Service注入运行时所依赖的对象。在Controller和Service创建对象的语句上方添加**@AutoWired**注解,就会在容器中自动查找所需的对象。

2.3.1 IOC详解

要把某个对象交给IOC容器管理,需要在对应的类上加上如下注解之一:

注意:声明bean的时候,可以通过注解的value属性指定bean的名字,如果没有指定,默认为类名首字母小写。

  • 前面声明bean的四大注解,要想生效,还需要被组件扫描注解@ComponentScan扫描。
  • 该注解虽然没有显式配置,但是实际上已经包含在了启动类声明注解 @SpringBootApplication中,默认扫描的范围是启动类所在包及其子包

所在包及其子包:

2.3.2 DI详解

基于@AutoWired进行依赖注入的常见方式有如下三种:

属性注入:

构造函数注入:

setter注入:


@AutoWired注解,默认是按照类型注入的。

如果存在多个相同类型的Bean,将会报错

解决方案:

相关推荐
IT_陈寒24 分钟前
Redis内存爆了,原来我漏掉了这个致命配置
前端·人工智能·后端
小bo波1 小时前
从"任意文件复制"深挖Java I/O:字符流与字节流的本质抉择
java·nio·io流·后端开发·文件复制
fliter1 小时前
最后一块拼图:用 bitvec 构造 IPv4 包,真正做出自己的 Ping
后端
fliter2 小时前
用 Rust 解析并生成 ICMP 包:checksum、nom 与 cookie-factory
后端
蝎子莱莱爱打怪2 小时前
XZLL-IM干货系列 03|消息 ID 设计:一个 UUID 搞不定的事,我用两个 ID 解决了
后端·面试·开源
fliter2 小时前
从 panic 到 Result:用 Rust 重新整理一个 ping 项目的错误处理
后端
森蓝情丶3 小时前
我给 AI 搭了个法庭:一个前端仔的 LangGraph 实战全记录
前端·后端
JensCS猿3 小时前
从 Spring Boot 回看 SSM 框架:手动挡与自动挡的驾驶哲学
后端
爱勇宝3 小时前
干了近 8 年,一夜之间被裁:AI 时代,程序员最该害怕的不是 AI
前端·后端·程序员
科米米3 小时前
嵌入式日志模块
后端