目录
4.1 典型场景:Request 作用域 Bean 注入到 Singleton Bean
一、依赖注入(DI)核心概念回顾
依赖注入是 Spring 框架的核心机制之一,通过将对象的依赖关系外部化,实现松耦合设计。Spring 支持三种注入方式:
- 构造函数注入:通过构造函数参数提供依赖。
- Setter 方法注入:通过 setter 方法提供依赖。
- 字段注入:通过
@Autowired
等注解直接注入到字段。
高级特性概览
本文将深入探讨以下 DI 高级特性:
- 依赖注入的解析策略
- 限定符(Qualifier)与自定义注解
- 作用域代理(Scoped Proxy)
- 延迟注入(Lazy Initialization)
- 条件注入(Conditional Bean)
- 动态代理与 AOP 增强
- 生命周期回调与后置处理器
二、依赖注入的解析策略
Spring 解析依赖时遵循以下策略:
- 类型匹配:优先通过 Bean 的类型查找匹配项。
- 名称匹配:若存在多个同类型 Bean,通过 Bean 名称匹配(如字段名、参数名)。
- 限定符(Qualifier):使用
@Qualifier
注解明确指定 Bean。
示例:多实现类的依赖注入
// 接口定义
public interface PaymentService {
void processPayment(double amount);
}
// 实现类1
@Service("alipayService")
public class AlipayServiceImpl implements PaymentService { ... }
// 实现类2
@Service("wechatPayService")
public class WechatPayServiceImpl implements PaymentService { ... }
// 依赖注入
@Service
public class OrderService {
// 方式1:通过 @Qualifier 指定
@Autowired
@Qualifier("alipayService")
private PaymentService paymentService;
// 方式2:通过字段名匹配
@Autowired
private PaymentService wechatPayService;
}
三、限定符(Qualifier)与自定义注解
3.1 @Qualifier 注解
@Qualifier
用于在多个同类型 Bean 中明确指定要注入的 Bean:
@Service
public class ShoppingCartService {
@Autowired
@Qualifier("promotionPriceCalculator")
private PriceCalculator priceCalculator;
}
3.2 自定义限定符注解
通过元注解 @Qualifier
创建自定义注解,简化依赖注入:
@Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Promotion {
// 自定义属性
String value() default "";
}
// 使用自定义注解
@Service
@Promotion
public class PromotionPriceCalculator implements PriceCalculator { ... }
// 注入时直接使用自定义注解
@Service
public class CheckoutService {
@Autowired
@Promotion
private PriceCalculator priceCalculator;
}
四、作用域代理(Scoped Proxy)
当依赖的 Bean 作用域与当前 Bean 不一致时,使用作用域代理解决依赖冲突。
4.1 典型场景:Request 作用域 Bean 注入到 Singleton Bean
// Request 作用域 Bean
@Service
@Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
public class UserSession {
private String userId;
// getter/setter
}
// Singleton Bean 注入 Request 作用域 Bean
@Service
public class OrderService {
@Autowired
private UserSession userSession; // 实际注入的是代理对象
public void createOrder() {
String userId = userSession.getUserId(); // 运行时动态代理到当前请求
}
}
4.2 代理模式选择
ScopedProxyMode.TARGET_CLASS
:基于 CGLIB 的类代理。ScopedProxyMode.INTERFACES
:基于接口的 JDK 动态代理。
五、延迟注入(Lazy Initialization)
通过 @Lazy
注解延迟 Bean 的初始化,直到首次使用时才创建。
5.1 应用场景
- 启动性能优化:减少容器启动时创建的 Bean 数量。
- 循环依赖处理:打破循环依赖的初始化死锁。
// 方式1:类级别注解
@Service
@Lazy
public class ExpensiveService {
public ExpensiveService() {
System.out.println("ExpensiveService initialized");
}
}
// 方式2:注入点注解
@Service
public class ClientService {
@Autowired
@Lazy
private ExpensiveService expensiveService;
}
六、条件注入(Conditional Bean)
使用 @Conditional
及其派生注解,根据条件决定是否创建 Bean。
6.1 内置条件注解
注解 | 作用 |
---|---|
@ConditionalOnBean | 当指定 Bean 存在时创建 |
@ConditionalOnMissingBean | 当指定 Bean 不存在时创建 |
@ConditionalOnProperty | 当配置属性满足条件时创建 |
@ConditionalOnClass | 当指定类存在于类路径时创建 |
@ConditionalOnMissingClass | 当指定类不存在于类路径时创建 |
6.2 自定义条件
public class OnProductionEnvironmentCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
Environment env = context.getEnvironment();
return "production".equals(env.getProperty("spring.profiles.active"));
}
}
// 使用自定义条件
@Configuration
public class AppConfig {
@Bean
@Conditional(OnProductionEnvironmentCondition.class)
public DataSource productionDataSource() {
// 创建生产环境数据源
}
}
七、动态代理与 AOP 增强
Spring 在依赖注入时会自动处理 AOP 代理,确保增强逻辑生效。
7.1 代理创建时机
- 基于接口的代理:当 Bean 实现了至少一个接口时,使用 JDK 动态代理。
- 基于类的代理:当 Bean 没有实现接口时,使用 CGLIB 代理。
7.2 示例:事务代理
@Service
@Transactional
public class ProductService {
public void updateStock(String productId, int quantity) {
// 业务逻辑
}
}
// 注入时自动获取代理对象
@Service
public class OrderProcessingService {
@Autowired
private ProductService productService; // 实际注入的是事务代理对象
}
八、生命周期回调与后置处理器
8.1 生命周期回调接口
InitializingBean
:在属性设置后执行初始化逻辑。DisposableBean
:在 Bean 销毁前执行清理逻辑。
8.2 自定义初始化 / 销毁方法
@Service
public class DatabaseConnection {
// 自定义初始化方法
@PostConstruct
public void init() {
// 初始化连接
}
// 自定义销毁方法
@PreDestroy
public void cleanup() {
// 关闭连接
}
}
8.3 BeanPostProcessor
通过实现 BeanPostProcessor
接口,在 Bean 初始化前后进行自定义处理:
@Component
public class CustomBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
// 初始化前处理
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
// 初始化后处理(如创建代理)
return bean;
}
}
九、最佳实践
- 优先使用构造函数注入:避免
NullPointerException
,支持不可变对象。 - 明确限定符:在多实现类场景中使用
@Qualifier
或自定义注解。 - 谨慎使用作用域代理:仅在必要时使用,避免过度复杂。
- 合理延迟初始化:对启动耗时的 Bean 使用
@Lazy
。 - 利用条件注解:根据环境或配置动态选择 Bean 实现。
- 避免循环依赖:重构代码设计,或使用
@Lazy
打破循环。
十、总结
Spring 的依赖注入高级特性提供了强大而灵活的对象管理能力,通过限定符、作用域代理、条件注入等机制,开发者可以更精细地控制 Bean 的创建和依赖关系。理解这些特性有助于构建更高效、更具扩展性的 Spring 应用,同时避免常见的依赖注入陷阱。