图源:简书 (jianshu.com)
Spring 通过注解实现 DI(依赖注入),本文详细讨论这些注解。
@Autowired
是 Spring 定义的注解,属于包org.springframework.beans.factory.annotation
。
@Autowired
匹配 bean 的顺序是:
@Autowired
是最常用的注解,用它可以通过属性注入、Setter注入、构造器注入来实现DI。在前文中讨论过这些内容,这里不再重复。
一般来说,用@Autowired
注入时必须存在类型匹配的唯一的 bean,没有匹配到和匹配到一个以上的 bean 都会报错:
@ToString
@EqualsAndHashCode
public abstract class Carrier {
protected final String maker;
protected final String model;
protected Carrier(String maker, String model) {
this.maker = maker;
this.model = model;
}
}
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true)
public class Car extends Carrier {
public Car(final String maker, final String model) {
super(maker, model);
}
}
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true)
public class Motorcycle extends Carrier {
public Motorcycle(final String maker, final String model) {
super(maker, model);
}
}
@Service
public class CarrierService {
@Autowired
private Carrier defaultCarrier;
public Carrier getDefaultCarrier() {
return defaultCarrier;
}
}
@Configuration
public class WebConfig {
@Lazy
@Autowired
private CarrierService carrierService;
@Bean
Car defaultCar(){
return new Car("长安汽车","X5");
}
@Bean
Motorcycle defaultMotorcycle(){
return new Motorcycle("哈雷","A7");
}
@Bean
ApplicationRunner applicationRunner(){
return args -> {
Carrier defaultCarrier = carrierService.getDefaultCarrier();
System.out.println(defaultCarrier);
};
}
}
这里定义的两个 bean 都具备Carrier
类型,因此用@Autowired
注入Carrier defaultCarrier
属性时会报错,Spring 不知道要注入的是哪一个。
可以通过@Qualifier
缩小筛选范围:
@Service
public class CarrierService {
@Autowired
@Qualifier("defaultCar")
private Carrier defaultCarrier;
// ...
}
示例中使用@Qualifier("defaultCar")
将注入限定为名称为defaultCar
的 bean,因此可以正常注入。
除了使用限定符,还可以使用名称进行筛选,@Autowired
使用属性名作为 bean 名称进行匹配:
@Service
public class CarrierService {
@Autowired
private Carrier defaultCar;
// ...
}
在用于名称匹配时,
@Autowired
并不好用,因为它只能用属性名作为 bean 名称匹配,@Resource
则可以用name
属性指定 bean 名称,而@inject
可以使用@Named
注解指定 bean 名称。
@Qualifier
还可以更灵活地对 bean 进行筛选,详细内容可以阅读我的另一篇文章。
除了@Qualifier
,还可以使用@Primary
指定一个主要的 bean 作为有多个备选项时的首选注入,详细情况可以参考我的同一篇文章。
@Resource
是 Java 扩展包的注解,属于包javax.annotation.Resource
。
@Autowired
按照类型优先进行匹配,@Resource
按照名称优先进行匹配,其匹配顺序是:
修改之前的示例,使用@Resource
进行依赖注入:
@Service
public class CarrierService {
@Resource
private Carrier defaultCarrier;
// ...
}
会报错,因为不能按照defaultCarrier
找到 bean,如果按照类型Carrier
匹配,能匹配到2个。
可以通过@Resource
的name
属性指定要匹配的 bean 名称:
@Service
public class CarrierService {
@Resource(name = "defaultCar")
private Carrier defaultCarrier;
// ...
}
或者也可以修改属性名称:
@Service
public class CarrierService {
@Resource
private Carrier defaultCar;
public Carrier getDefaultCarrier() {
return defaultCar;
}
}
或者按照类型匹配,提供一个唯一 bean 对应的类型:
@Service
public class CarrierService {
@Resource
private Car defaultCarrier;
// ...
}
最后,同样可以使用@Qualifier
:
@Service
public class CarrierService {
@Resource
@Qualifier("defaultCar")
private Carrier defaultCarrier;
// ...
}
@Resource
还可以通过 Setter 注入,用法与属性注入一致,这里不再说明。
@Inject
同样属于 Java 扩展包,属于包javax.inject.Inject
。
@Inject
的匹配顺序是:
要使用@Inject
注解要先添加依赖:
<dependency>
<groupId>jakarta.injectgroupId>
<artifactId>jakarta.inject-apiartifactId>
<version>2.0.1version>
dependency>
先看按类型匹配的示例:
@Service
public class CarrierService {
@Inject
private Car defaultCarrier;
// ...
}
如果按类型匹配出多个候选项,可以通过限定符进一步匹配:
@Service
public class CarrierService {
@Inject
@Qualifier("defaultCar")
private Carrier defaultCarrier;
// ...
}
也可以通过名称进一步匹配:
@Service
public class CarrierService {
@Inject
@Named("defaultMotorcycle")
private Carrier defaultCarrier;
// ...
}
@Named
同样属于 Java 扩展包,属于包jakarta.inject.Named
。
如果
@Named
注解没有起作用,可以检查一下 maven 依赖中的jakarta.inject
模块的版本是否正确。
@Inject
用于 Setter 注入的方式与属性注入类似,这里不再赘述。
@Inject
也可以用于构造器注入:
@Service
public class CarrierService {
private Carrier defaultCarrier;
@Inject
public CarrierService(@Named("defaultCar") Carrier defaultCarrier) {
this.defaultCarrier = defaultCarrier;
}
// ...
}
如果需要按照类型优先匹配,可以使用@Autowired
或@inject
,如果需要按照 bean 名称优先匹配,可以使用@Resource
。