IoC 和 DI 是 Spring 中最重要的两个概念,其中 IoC 为控制反转的思想,而 DI 依赖注入是 IoC 的具体实现。


# 概述

在 Spring 中实现依赖注入的常见方式有以下 3 种:

  1. 属性注入(Field Injection);
  2. Setter 注入(Setter Injection);
  3. 构造方法注入(Constructor Injection)。

# 属性注入

属性注入是日常开发中使用最多的一种注入方式:

1
2
3
4
5
6
7
8
9
10
11
@RestController
public class UserController {
// 属性对象
@Autowired
private UserService userService;

@RequestMapping("/add")
public UserInfo add(String username, String password) {
return userService.add(username, password);
}
}
# 优点

属性注入最大的优点就是实现简单、使用简单,只需要给变量上添加一个注解(@Autowired),就可以在不 new 对象的情况下,直接获得注入的对象了(这就是 DI 的功能和魅力所在),所以它的优点就是使用简单。

# 缺点

属性注入虽然使用简单,但是也存在很多问题,甚至编译器 Idea 都会提醒你 “不建议使用此注入方式”,Idea 的提示信息如下:

image-20220827130311145

属性注入的缺点主要包含以下 3 个:

  1. 功能性问题:无法注入一个不可变的对象(final 修饰的对象);
  2. 通用性问题:只能适应于 IoC 容器;
  3. 设计原则问题:更容易违背单一设计原则。

# Setter 注入

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@RestController
public class UserController {
// Setter 注入
private UserService userService;

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

@RequestMapping("/add")
public UserInfo add(String username, String password) {
return userService.add(username, password);
}
}
# 优点

完全符合单一职责的设计原则,因为每一个 Setter 只针对一个对象。

# 缺点
  1. 不能注入不可变对象(final 修饰的对象);
  2. 注入的对象可被修改。

# 构造方法注入

构造方法注入是 Spring 官方从 4.x 之后推荐的注入方式,它的实现代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@RestController
public class UserController {
// 构造方法注入
private UserService userService;

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

@RequestMapping("/add")
public UserInfo add(String username, String password) {
return userService.add(username, password);
}
}

当然,如果当前的类中只有一个构造方法,那么 @Autowired 也可以省略,所以以上代码还可以这样写:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@RestController
public class UserController {
// 构造方法注入
private UserService userService;

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

@RequestMapping("/add")
public UserInfo add(String username, String password) {
return userService.add(username, password);
}
}
# 优点
  1. 可注入不可变对象;
  2. 注入对象不会被修改;
  3. 注入对象会被完全初始化;
  4. 通用性更好。