通过写一个完整的 Spring 示例 ,展示同时存在两个实现类时,如何用 @Resource 或 @Qualifier 精确选择注入。
现有代码
- 定义接口
java
public interface Processor {
void process();
}
- 两个实现类
java
import org.springframework.stereotype.Component;
@Component("creditCardProcessor")
public class CreditCardProcessor implements Processor {
@Override
public void process() {
System.out.println("Processing credit card...");
}
}
@Component("zhiFuBaoProcessor")
public class ZhiFuBaoProcessor implements Processor {
@Override
public void process() {
System.out.println("Processing zhifubao...");
}
}
第一种:使用 @Resource 指定注入(最常见)
java
import javax.annotation.Resource;
import org.springframework.stereotype.Service;
@Service
public class PaymentService1 {
// 按名字注入,必须和 Bean 名字一致
@Resource(name="creditCardProcessor")
private Processor processor;
public void pay() {
processor.process();
}
}
第二种:使用构造函数注入 + @Qualifier(Spring 官方推荐)
java
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
@Service
public class PaymentService2 {
private final Processor processor;
// 构造函数注入,指定要注入的 Bean
public PaymentService2(@Qualifier("creditCardProcessor") Processor processor) {
this.processor = processor;
}
public void pay() {
processor.process();
}
}
容器里只有一个
Processor类型的 Bean(比如只有CreditCardProcessor),Spring 会直接注入它,不需要@Qualifier。
第三种:使用 @Autowired + @Qualifier 指定注入
java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
@Service
public class PaymentService3 {
@Autowired
@Qualifier("zhiFuBaoProcessor")
private Processor processor;
public void pay() {
processor.process();
}
}
测试运行
java
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
@Bean
CommandLineRunner run(PaymentService1 paymentService1, PaymentService2 paymentService2, PaymentService3 paymentService3) {
return args -> {
paymentService1.pay(); // 输出:Processing credit card...
paymentService2.pay(); // 输出:Processing credit card...
paymentService3.pay(); // 输出:Processing zhifubao...
};
}
}
IoC 的好处
Spring IoC 容器的一个好处:调用方不需要修改,只要容器负责把合适的依赖注入进去。
字段注入和构造函数注入的共同点:
- 解耦调用方 :调用
PaymentService的地方不需要关心具体用的是CreditCardProcessor还是ZhiFuBaoProcessor。 - 切换实现简单:只要改 Bean 配置或注解,调用代码完全不用动。
- 由 IoC 容器负责依赖管理:容器在启动时扫描、创建 Bean,并根据注解或配置自动注入。
字段注入和构造函数注入的区别:
| 特性 | 构造函数注入 | @Resource 字段注入 |
|---|---|---|
| 注入时机 | 对象创建时必须提供依赖 | 对象创建后由容器反射赋值 |
| 空值风险 | 无,构造函数强制依赖 | 有可能出现 null(如果容器没找到匹配 Bean) |
| 测试友好性 | 更方便单元测试(直接传 mock) | 测试时需要容器或反射赋值 |
| 推荐程度 | Spring 官方更推荐 | 常见但略逊于构造函数注入 |