掌握 Spring 的依赖注入(DI)高级特性

目录

一、依赖注入(DI)核心概念回顾

高级特性概览

二、依赖注入的解析策略

示例:多实现类的依赖注入

三、限定符(Qualifier)与自定义注解

3.1 @Qualifier 注解

3.2 自定义限定符注解

四、作用域代理(Scoped Proxy)

4.1 典型场景:Request 作用域 Bean 注入到 Singleton Bean

4.2 代理模式选择

五、延迟注入(Lazy Initialization)

5.1 应用场景

六、条件注入(Conditional Bean)

6.1 内置条件注解

6.2 自定义条件

七、动态代理与 AOP 增强

7.1 代理创建时机

7.2 示例:事务代理

八、生命周期回调与后置处理器

8.1 生命周期回调接口

8.2 自定义初始化 / 销毁方法

8.3 BeanPostProcessor

九、最佳实践

十、总结


一、依赖注入(DI)核心概念回顾

依赖注入是 Spring 框架的核心机制之一,通过将对象的依赖关系外部化,实现松耦合设计。Spring 支持三种注入方式:

  • 构造函数注入:通过构造函数参数提供依赖。
  • Setter 方法注入:通过 setter 方法提供依赖。
  • 字段注入:通过 @Autowired 等注解直接注入到字段。

高级特性概览

本文将深入探讨以下 DI 高级特性:

  1. 依赖注入的解析策略
  2. 限定符(Qualifier)与自定义注解
  3. 作用域代理(Scoped Proxy)
  4. 延迟注入(Lazy Initialization)
  5. 条件注入(Conditional Bean)
  6. 动态代理与 AOP 增强
  7. 生命周期回调与后置处理器

二、依赖注入的解析策略

Spring 解析依赖时遵循以下策略:

  1. 类型匹配:优先通过 Bean 的类型查找匹配项。
  2. 名称匹配:若存在多个同类型 Bean,通过 Bean 名称匹配(如字段名、参数名)。
  3. 限定符(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;
    }
}

九、最佳实践

  1. 优先使用构造函数注入:避免 NullPointerException,支持不可变对象。
  2. 明确限定符:在多实现类场景中使用 @Qualifier 或自定义注解。
  3. 谨慎使用作用域代理:仅在必要时使用,避免过度复杂。
  4. 合理延迟初始化:对启动耗时的 Bean 使用 @Lazy
  5. 利用条件注解:根据环境或配置动态选择 Bean 实现。
  6. 避免循环依赖:重构代码设计,或使用 @Lazy 打破循环。

十、总结

Spring 的依赖注入高级特性提供了强大而灵活的对象管理能力,通过限定符、作用域代理、条件注入等机制,开发者可以更精细地控制 Bean 的创建和依赖关系。理解这些特性有助于构建更高效、更具扩展性的 Spring 应用,同时避免常见的依赖注入陷阱。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

潜意识Java

源码一定要私信我,有问题直接问

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值