DI 依赖注入
@Autowired 注入
1. 属性注入
示例1:容器中 Person 类型的 bean 只有一个
@Autowired
private Person person; // 属性名换成 person123、user 都可以
示例2:容器中 Person 类型的 bean 有多个
@Bean
public Person marry(){ return new Person(); }
@Bean
public Person bob(){ return new Person(); }
注入结果情况:
@Autowired
private Person asdf; // 注入失败。Person 有两个,会根据名称再次匹配,但是没有 asdf 的,项目启动时就会报错
@Autowired
private Person marry; // 注入成功。根据名称能确定唯一
@Autowired
private Person bob; // 注入成功。同上,根据名称也能确定唯一
@Autowired
private List<Person> personList; // 注入成功。根据类型获取多个
@Autowired
private Map<String, Person> personMap; // 注入成功。也是根据类型获取多个(key 是 bean 名称,value 是 bean 对象)
@Autowired
private ApplicationContext applicationContext; // ICO 容器也能注入
上面示例中第一种方式的解决方案有2个
-
注册 bean 时,给任一 bean 加
@Primary注解@Primary // 当 asdf 找不到而系统又有多个 Person 的 bean,当前的优先使用 @Bean public Person marry(){ return new Person(); } -
注入 bean 时,使用
@Qualifier("marry")获取指定名称的 bean@Qualifier("marry") // 这时 asdf 就无效了,会使用 marry 再次匹配 @Autowired private Person asdf;
注入 bean 时,根据类型如果一个都找不到,还可以这样配置
@Autowired(required=false) // 只适用于根据类型一个都找不到;如果根据类型能找到多个,根据名称找不到,这种情况是不适用的
private Person asdf;
2. 构造注入
因为不推荐属性注入,所以构造注入的规则一直在调整从而更灵活兼容更多可能,在不同的 Spring 版本中表现也不一致
- 只有一个构造方法,可以不用加
@Autowired(Spring4.3及之后,Spring4.3 之前哪怕一个构造也要加) - 有多个构造方法,必须在其中一个构造上加
@Autowired,如果都不加就会调用无参构造(没无参就报错;有无参不报错但属性都是 null)
示例1:多个构造,都不加 @Autowired
private User user3;
private Person person;
public TestController(User user3) {
this.user3 = user3;
}
public TestController(Person person) {
this.person = person;
}
public TestController() { // 选取这个无参构造反射创建对象(user3 和 person 会是 null)。要是没有无参启动会报错
}
示例2:一个构造,可省略 @Autowired
private User user3;
private Person person;
public TestController(User user3) { // 没得选,只能使用这个,可以不用加 @Autowired(person 会是 null)
this.user3 = user3;
}
示例3:多个构造,其中一个加 @Autowired
private User user3;
private Person person;
public TestController(User user3) {
this.user3 = user3;
}
@Autowired
public TestController(Person person) { // 选择这个构造
this.person = person;
}
public TestController() {
}
示例4:设置属性不注入
private User user3; // 假设这本来就不是一个 bean
private Person person;
@Autowired
public TestController(@Autowired(required=false)) User user3, Person person) {
this.user3 = user3;
this.person = person;
}
3. 方法注入
示例1:setter 方法
@Component
public class Test {
// 属性名无所谓
private Person asdfasdf;
// 如果 Person 的 bean 是多个,也能使用 @Qualifier 或 @Primary 解决
@Autowired
public void setPerson(@Qualifier("marry") Person person) {
this.asdfasdf = person;
}
}
示例2:不是 setter 也能注入
@Component
public class Test {
private Person asdfasdf;
private User user;
@Autowired
public void otherMethod(@Qualifier("marry") Person person, User user) { // 不是 setter 也会自动注入
this.asdfasdf = person;
this.user = user;
}
}
@Resource 注入
这是 JDK 提供的注解,也能完成自动注入,和 @Autowired 相比有以下区别:
- 优先根据名称获取 bean,如果获取不到,再根据类型获取
- 不支持
required=false - 不支持构造注入(不能标注在构造方法上)
- 方法注入时参数只能是一个(
@Autowired不限制)
示例1:容器中 User 类型的 bean 只有一个
@Resource
private User user11111; // 优先根据名称找,找不到再根据类型找
示例2:容器中 User 类型的 bean 有多个
假设系统中 User 类型的 bean 有两个:user1 和 user2
@Resource
private User user11111; // 根据名称找不到,再根据类型找,找到两个,启动报错
解决办法和 @Autowired 一样也有两个:
1️⃣ 注册 bean 时,给任一bean添加 @Primary 注解
2️⃣ 注入 bean 时,指定名称。@Autowired 是配合 @Qualifier 来指定,@Resource 是通过 name 属性来指定
@Resource(name="user1")
private User user11111; // 会使用 user1 去找,能找到一个,直接注入
示例3:方法注入
方法注入时也只能单个属性注入(所以 @Resource 都是用作属性注入而不是方法注入)
private Person person;
private User user;
@Resource
public void otherMethod(User user) {
this.user = user;
}
@Resource
public void otherMethod(Person person) {
this.person = person;
}
下面这种会报错:
private Person person;
private User user;
@Resource
public void otherMethod(Person person, User user) { // 同时注入多个属性,是不支持的
this.person = person;
this.user = user;
}
@Inject 注入
-
也是来于 JDK,
@Resource是 JSR250 的规范,@Inject是 JSR330 的规范 -
功能上比
@Resource更强大(支持构造注入,也是先根据类型匹配。和 @Autowired 大部分功能重叠) -
需要引入额外的 maven 依赖
<!-- JSR-330 @Inject 标准注解 --> <dependency> <groupId>javax.inject</groupId> <artifactId>javax.inject</artifactId> <version>1</version> <!-- 最新版本为 1 --> </dependency>
注入AOP的属性
public interface IPerson{
public void sayHell0();
}
public class Man implements IPerson{
@Override
public void sayHell0(){};
}
@Aspect
@Conponent
public class MyAspect{
@Before("execution(* com.example.service.Man.*(..))") // 增强 Man 的方法
public void logUserId() {
}
}
/**
* 在 SpringBoot 中有可能会报错,因为 AOP 会生成代理对象,实际注入的是代理类而不是原始的 Man 对象,而是代理对象
* 如果使用 cglib 方式,代理对象的类是继承自 Man,所以不会报错
* 如果使用 JDK 方式,代理对象的类是 IPerson 的子类(IPerson 两个子类Man和生成的代理类)所以强转会报错,解决办法就是使用 IPerson 替换 Man
*/
@Autowired
private Man man; // 如果是 JDK 动态代理,换成 private IPerson man;

浙公网安备 33010602011771号