分层解耦

web开发中的三层架构,IOC与DI介绍

分层解耦

三层架构

在web开发中,一般有三层结构,从第一层到第三层分别为控制层(controller)、业务逻辑层(service)、数据访问层(dao)

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

service:业务逻辑层,处理具体的业务逻辑。

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

三层拆分前的代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@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;
    }

三层拆分后的代码:

image-20250803220945283

IOC与DI入门

如果按照刚刚Controller层中用new对象获取Service的方法,即

1
private UserService userService = new UserServiceImpl();

按照这种形式获取的话,如果有多个实现类需要切换就得频繁修改代码,为了解决这个问题,spring提供了控制反转依赖注入

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

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

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

所以要将DAO和Service层的实现类交给IOC容器管理,并为Controller和Service注入运行时所依赖的对象

要做到上述操作只需要在实现类上方加上@Component,在Service和DAO成员变量上方加上@Autowired即可,例如

1
2
3
4
5
6
7
@Component //将当前类交给spring管理, 声明为spring容器中bean对象
public class UserDaoImpl implements UserDao

-----------------------------------------------
    
@Autowired //应用程序运行时,会自动查询该类型的bean对象,并赋值给该成员变量
private UserService userService;

IOC详解

常见Bean注解

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

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

在idea中查看Bean

在idea中,启动项目后可以查看Bean

image-20250804211340619

点击添加依赖后重新启动项目,再打开此处即可查看

image-20250804211521390

Bean的默认名字为首字母小写的类名,可以自定义Bean的名字,如图所示

image-20250804211850319

Bean的生效范围

前面声明bean的四大注解,要想生效,还需要被组件扫描注解@ComponentScan扫描。

该注解虽然没有显式配置,但是实际上已经包含在了启动类声明注解 @SpringBootApplication 中,默认扫描的范围是启动类所在包及其子包。

image-20250804212329526 image-20250804212502950

DI详解

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

属性注入

1
2
3
4
5
6
@RestController 
public class UserController {    
    @Autowired    
    private UserService userService;
    //......
}

优点:代码简洁、方便快速开发

缺点:隐藏了类之间的依赖关系、可能会破坏类的封装性

构造函数注入(构造器注入)

1
2
3
4
5
6
7
8
9
@RestController
public class UserController {
    private final UserService userService;

    @Autowired
    public UserController(UserService userService) {
        this.userService = userService;
    }
}

优点:能清晰地看到类的依赖关系、提高了代码的安全性

缺点:代码繁琐、如果构造参数过多,可能会导致构造函数臃肿

注意:如果只有一个构造函数,@Autowired注解可以省略

setter注入

1
2
3
4
5
6
7
8
9
@RestController
public class UserController {
    private UserService userService;

    @Autowired
    public void setUserService(UserService userService) {
        this.userService = userService;
    }
}

优点:保持了类的封装性,依赖关系更清晰

缺点:需要额外编写setter方法,增加了代码量

@Autowired的注意事项

@Autowired注解默认是按照类型进行注入的,如果存在多个相同类型的Bean(多个实现类),将会报错,比如

image-20250804223731789

有三种解决方式:

①**@Primary**

1
2
3
4
5
6
7
8
@Primary
@Service
public class UserServiceImpl implements UserService {
    @Override
    public List<User> list() {
        // 省略 ……
    }
}

②**@Qualifier**

1
2
3
4
5
6
@RestController
public class UserController {
    @Autowired
    @Qualifier("userServiceImpl")
    private UserService userService;
}

③**@Resource**

1
2
3
4
5
@RestController
public class UserController {
    @Resource(name = "userServiceImpl")
    private UserService userService;
}

@Resource 与 @Autowired区别 ?

@Autowired是Spring框架提供的注解,而@Resource是JavaEE规范提供的

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

本站于2025年3月26日建立
使用 Hugo 构建
主题 StackJimmy 设计