您好,登录后才能下订单哦!
# Spring DI依赖注入方式和区别有哪些
## 目录
1. [依赖注入(DI)核心概念](#一依赖注入di核心概念)
2. [三种主要依赖注入方式](#二三种主要依赖注入方式)
- [2.1 构造器注入](#21-构造器注入)
- [2.2 Setter方法注入](#22-setter方法注入)
- [2.3 字段注入](#23-字段注入)
3. [其他特殊注入方式](#三其他特殊注入方式)
- [3.1 接口注入](#31-接口注入)
- [3.2 方法注入](#32-方法注入)
4. [不同注入方式的对比分析](#四不同注入方式的对比分析)
5. [Spring官方推荐实践](#五spring官方推荐实践)
6. [实际开发中的选择策略](#六实际开发中的选择策略)
7. [常见问题与解决方案](#七常见问题与解决方案)
---
## 一、依赖注入(DI)核心概念
依赖注入(Dependency Injection)是Spring框架实现控制反转(IoC)的核心机制,通过将对象依赖关系的创建和管理从代码内部转移到外部容器,实现组件间的解耦。
**核心价值**:
- 降低耦合度
- 提高可测试性
- 增强代码可维护性
- 简化复杂对象创建
```java
// 传统方式(紧耦合)
class ServiceA {
private ServiceB serviceB = new ServiceB();
}
// DI方式(松耦合)
class ServiceA {
private ServiceB serviceB;
// 通过外部注入依赖
public ServiceA(ServiceB serviceB) {
this.serviceB = serviceB;
}
}
实现方式:
@Component
public class OrderService {
private final PaymentService paymentService;
@Autowired // Spring 4.3+ 可省略
public OrderService(PaymentService paymentService) {
this.paymentService = paymentService;
}
}
特点: - 强依赖性保障(对象创建时即完成注入) - 适合必须依赖项 - 天然支持不可变对象(final字段) - 明确的循环依赖检测
XML配置等效形式:
<bean id="orderService" class="com.example.OrderService">
<constructor-arg ref="paymentService"/>
</bean>
实现方式:
@Component
public class UserService {
private UserRepository userRepository;
@Autowired
public void setUserRepository(UserRepository userRepository) {
this.userRepository = userRepository;
}
}
特点: - 灵活性高(可重新注入) - 适合可选依赖项 - 支持JavaBean规范 - 可通过@Required注解标记必需依赖
XML配置等效形式:
<bean id="userService" class="com.example.UserService">
<property name="userRepository" ref="userRepository"/>
</bean>
实现方式:
@Component
public class ProductService {
@Autowired
private InventoryService inventoryService;
}
特点: - 代码简洁(最少样板代码) - 存在反射性能开销 - 难以进行单元测试(必须通过Spring容器) - 隐藏依赖关系
早期Spring版本通过实现特定接口完成注入,现已被注解方式取代。
@Component
@Scope("prototype")
public class ReportGenerator {
public void generate(ReportData data) {
// ...
}
}
@Component
public class ReportService {
@Autowired
private Provider<ReportGenerator> reportGeneratorProvider;
public void createReport() {
ReportGenerator generator = reportGeneratorProvider.get();
generator.generate(new ReportData());
}
}
适用场景: - 解决原型Bean注入单例Bean问题 - 延迟获取Bean实例
特性 | 构造器注入 | Setter注入 | 字段注入 |
---|---|---|---|
不可变性支持 | ✅ (final) | ❌ | ❌ |
循环依赖检测 | 立即 | 延迟 | 延迟 |
单元测试便利性 | 高(可直接new) | 中 | 低(需反射) |
代码简洁度 | 中 | 中 | 高 |
依赖必要性表达 | 明确 | 需额外注解 | 不明确 |
Spring官方推荐度 | 首选 | 次要选择 | 不推荐 |
适合场景 | 强依赖 | 可选依赖/配置 | 快速原型开发 |
性能影响: - 构造器注入:启动时一次性解决,运行时无开销 - Setter注入:可能存在动态代理开销 - 字段注入:反射操作带来轻微性能损耗
Spring 4.3+ 自动推断:单构造器时可省略@Autowired
@Service
public class TaxService {
private final Calculator calculator;
// 自动识别为注入点
public TaxService(Calculator calculator) {
this.calculator = calculator;
}
}
必要依赖使用构造器:
@RestController
public class PaymentController {
private final PaymentValidator validator;
public PaymentController(PaymentValidator validator) {
Assert.notNull(validator, "Validator must not be null");
this.validator = validator;
}
}
可选依赖使用Setter:
@Service
public class NotificationService {
private EmailSender emailSender;
@Autowired(required = false)
public void setEmailSender(EmailSender emailSender) {
this.emailSender = emailSender;
}
}
推荐组合方案: 1. 核心业务组件:构造器注入 2. 基础设施组件:Setter注入 3. 测试类/临时代码:字段注入(需配合@Mock)
循环依赖解决方案:
// 方案1:改用Setter注入
@Service
public class ServiceA {
private ServiceB serviceB;
@Autowired
public void setServiceB(ServiceB serviceB) {
this.serviceB = serviceB;
}
}
// 方案2:@Lazy延迟初始化
@Service
public class ServiceX {
private final ServiceY serviceY;
public ServiceX(@Lazy ServiceY serviceY) {
this.serviceY = serviceY;
}
}
Q1:该用@Autowired还是@Resource?
- @Autowired
:按类型装配,支持Spring的@Qualifier
- @Resource
:JSR-250标准,优先按名称匹配
Q2:多实现类如何注入?
@Autowired
private List<Validator> validators; // 收集所有实现
@Autowired
@Qualifier("primaryDataSource")
private DataSource dataSource;
Q3:测试时如何模拟依赖?
// 构造器注入的测试样例
class OrderServiceTest {
@Test
void testCreateOrder() {
PaymentService mockPayment = mock(PaymentService.class);
OrderService service = new OrderService(mockPayment);
// 测试逻辑...
}
}
最佳实践总结:在Spring Boot应用中,80%的场景应使用构造器注入,15%使用Setter注入,剩余5%特殊场景可考虑字段注入。保持代码的明确性和可测试性应是首要考虑因素。 “`
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。