依赖注入(DI)

在 Spring 中,依赖注入(Dependency Injection, DI)是核心特性。它通过 IoC 容器管理 Bean 的依赖关系,从而避免手动 new 对象,提升了代码的解耦性和可维护性。


2.1 @Autowired 自动装配

@Autowired 是 Spring 提供的注解,用于让容器自动完成依赖注入。

要实现自动注入,不一定非要写 @Autowired,因为 Spring 在某些场景下会自动完成:

  • @Bean 方法的参数会自动注入
  • 构造函数的参数会自动注入

特性

构造函数

  • 如果 Bean 只有一个有参构造函数,即使省略 @Autowired,Spring 也会自动注入参数
  • 如果有多个有参构造函数,且没有无参构造函数,会报错。此时需要用 @Autowired 明确指定
  • 构造函数上的 required 属性会失效

参数

  • 如果想设置某个构造函数参数不是必须的,可以单独标记
  • 在单元测试中也可以直接对方法参数使用

方法

  • Spring 会自动调用被 @Autowired 标记的方法,并完成参数注入

装配规则

  • Spring 会先根据 类型 去容器中查找 (byType)
  • 如果有多个同类型的 Bean,再根据 名字 查找 (byName)
  • 如果还是找不到,可以通过以下方式解决:
    • 使用 @Primary 指定某个 Bean 为主要候选
    • 使用 @Qualifier(“beanName”) 指定 Bean 的名字

容错处理

  • 如果容器中一个都没找到,会报错
  • 可以通过 @Autowired(required = false) 避免报错

示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
@Service
public class OrderService {
private final UserService userService;

// 推荐:构造函数注入
@Autowired
public OrderService(UserService userService) {
this.userService = userService;
}

// 方法注入
@Autowired
public void init(PaymentService paymentService) {
System.out.println("支付服务已注入: " + paymentService);
}
}

@Service
public class UserService {}

@Service
@Primary
public class PaymentService {}

@Service
public class PaymentServiceV2 {}
@Service
public class TestService {
// 使用 @Qualifier 指定注入哪个 Bean
@Autowired
@Qualifier("paymentServiceV2")
private PaymentService paymentService;
}

2.2 @Inject 和 @Resource

除了 @Autowired,我们还可以使用 JDK 提供的注解。

@Inject

  • JSR-330 标准
  • 和 @Autowired 类似,但不能设置 required = false

示例:

1
2
3
4
5
6
7
import javax.inject.Inject;

@Service
public class BookService {
@Inject
private UserService userService;
}

@Resource

  • JSR-250 标准
  • 先根据名字查找,再根据类型查找

示例:

1
2
3
4
5
6
7
import javax.annotation.Resource;

@Service
public class BlogService {
@Resource(name = "userService")
private UserService userService;
}

IDEA 为什么不建议在字段上使用 @Autowired

  1. 字段通常是 private,通过 @Autowired 注入会破坏封装性
  2. 依赖注入应通过构造函数或方法完成,便于单元测试和维护

推荐的做法:

  • 使用构造函数注入(最推荐)
  • 使用 @Resource 指定 Bean
  • 在字段上用 @Autowired 也不是错误,但不够优雅

2.3 @Value

@Value 用于给字段注入值,可以是常量、配置文件中的属性,或者表达式。

常见用法

直接赋值:

1
2
@Value("ceshi")
private String name;

引用配置文件属性:

1
2
3
4
# application.properties
user.age=20
@Value("${user.age}")
private Integer age;

外部配置文件(非 Spring Boot):
需要通过 @PropertySource 指定:

1
2
3
4
5
6
7
8
@Component
@PropertySource("classpath:xushu.properties")
public class User {
@Value("${xushu.name}")
private String name;
@Value("${xushu.age}")
private Integer age;
}

默认值:

1
2
@Value("${user.address:默认地址}")
private String address;

SpEL 表达式:

1
2
3
4
5
6
7
8
@Value("#{1+2}")
private Integer sum;

@Value("#{T(java.lang.Math).random() * 100}")
private Double random;

@Value("#{user.age > 18 ? '成年人' : '未成年'}")
private String status;

总结

依赖注入的推荐做法:

  • 优先使用构造函数注入,保证依赖的不可变性和可测试性
  • 在 Bean 选择时,合理使用 @Primary 或 @Qualifier
  • 配置值注入用 @Value,支持默认值和 SpEL 表达式
  • @Autowired 和 @Inject/@Resource 都可以用,根据习惯或团队规范选择