@DependsOn 详解及详细源码展示

@DependsOn 是 Spring 框架中用于显式声明 Bean 之间依赖关系的核心注解,通过强制指定被依赖 Bean 的初始化顺序,解决因依赖关系不明确导致的初始化问题(如未初始化的依赖被提前使用)。以下从注解定义、源码解析、核心功能、使用场景注意事项展开详细说明。


一、@DependsOn 注解的定义与源码解析

@DependsOn 位于 org.springframework.context.annotation 包中,其源码定义如下(简化版):

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DependsOn {

    /**
     * 依赖的 Bean 名称(支持多个,用逗号分隔)
     */
    String[] value() default {};
}

关键特性

  • 标记在 @Configuration 配置类或 @Bean 方法上,声明当前 Bean 依赖的其他 Bean。
  • 被依赖的 Bean 会在当前 Bean 之前初始化(确保当前 Bean 使用时依赖已就绪)。

二、核心功能:控制 Bean 的初始化顺序

Spring 容器的默认行为是根据 Bean 的依赖关系自动推导初始化顺序(如 A 依赖 B,则 B 先初始化)。但当依赖关系复杂或隐式时(如通过方法调用间接依赖),Spring 可能无法正确推导顺序,导致初始化错误。@DependsOn 的核心作用是显式声明依赖顺序,强制被依赖的 Bean 先于当前 Bean 初始化。

1. 工作流程

Spring 启动时,ConfigurationClassParser 会扫描所有 @Configuration 类和 @Bean 方法上的 @DependsOn 注解,并通过 DependsOnAnnotationBeanPostProcessor 调整 Bean 的初始化顺序。具体流程如下:

  1. 解析 @DependsOn 注解:获取当前 Bean 依赖的 Bean 名称列表。
  2. 验证依赖存在性:检查被依赖的 Bean 是否已声明(否则抛 NoSuchBeanDefinitionException)。
  3. 调整初始化顺序:将被依赖的 Bean 标记为“需优先初始化”,确保在当前 Bean 之前完成初始化。

2. 与自动依赖推导的区别

Spring 默认通过以下方式自动推导依赖顺序:

  • 构造器注入:若 Bean A 的构造器参数是 Bean B,则 B 先初始化。
  • @Autowired 字段/方法注入:若 Bean A 注入 Bean B,则 B 先初始化。

@DependsOn 适用于以下场景:

  • 依赖关系未通过构造器/@Autowired 显式声明(如通过方法调用间接依赖)。
  • 需要强制指定顺序(即使自动推导顺序与预期不符)。

三、典型使用场景与示例

1. 显式声明隐式依赖

当 Bean A 通过方法调用间接依赖 Bean B(未通过构造器/@Autowired 注入),Spring 无法自动推导依赖顺序,需用 @DependsOn 显式声明。

示例

@Configuration
public class ServiceConfig {

    // 未直接注入,但 ServiceA 会调用 ServiceB 的方法
    @Bean
    public ServiceA serviceA() {
        return new ServiceA(); // 内部调用 serviceB.doSomething()
    }

    @Bean
    @DependsOn("serviceA") // 错误!ServiceA 依赖 ServiceB,但此处声明反了
    public ServiceB serviceB() {
        return new ServiceB();
    }
}

正确方式

@Configuration
public class ServiceConfig {

    @Bean
    @DependsOn("serviceB") // ServiceA 依赖 ServiceB,声明 ServiceB 先初始化
    public ServiceA serviceA(ServiceB serviceB) { // 构造器注入 ServiceB(显式依赖)
        return new ServiceA(serviceB); // 内部使用 serviceB
    }

    @Bean
    public ServiceB serviceB() {
        return new ServiceB();
    }
}

2. 有状态 Bean 的初始化顺序

有状态 Bean(如保存用户会话的 UserSessionManager)需要依赖无状态 Bean(如 CacheService),需确保 CacheService 先初始化。

示例

@Configuration
public class SessionConfig {

    @Bean
    @DependsOn("cacheService") // UserSessionManager 依赖 CacheService
    public UserSessionManager userSessionManager(CacheService cacheService) {
        return new UserSessionManager(cacheService); // 注入 CacheService
    }

    @Bean
    public CacheService cacheService() {
        return new RedisCacheService(); // 无状态 Bean
    }
}

3. 第三方库 Bean 的初始化顺序

集成第三方库时,需确保库的初始化 Bean 先于业务 Bean 执行(如 MyBatis 的 SqlSessionFactory 需先于 Mapper 初始化)。

示例

@Configuration
public class MyBatisConfig {

    @Bean
    @DependsOn("dataSource") // SqlSessionFactory 依赖 DataSource
    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) {
        SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
        factoryBean.setDataSource(dataSource);
        return factoryBean.getObject();
    }

    @Bean
    public DataSource dataSource() {
        return new HikariDataSource();
    }
}

四、源码实现细节与关键类

1. DependsOnAnnotationBeanPostProcessor

Spring 处理 @DependsOn 的核心类,继承自 InstantiationAwareBeanPostProcessorAdapter,负责调整 Bean 的初始化顺序。其关键逻辑如下:

(1)扫描 @DependsOn 注解

在解析 @Configuration 类或 @Bean 方法时,扫描 @DependsOn 注解,获取依赖的 Bean 名称列表。

(2)注册依赖关系

将当前 Bean 与依赖的 Bean 建立依赖关系(通过 BeanDefinitionsetDependsOn 方法),确保被依赖的 Bean 先初始化。

2. BeanDefinitiondependsOn 属性

Spring 的 BeanDefinition 接口有一个 dependsOn 方法,用于声明当前 Bean 依赖的其他 Bean 名称。DependsOnAnnotationBeanPostProcessor 会将该属性设置为 @DependsOn 注解的 value,从而触发初始化顺序的调整。


五、注意事项与常见问题

1. 循环依赖的处理

@DependsOn 无法解决循环依赖(如 A 依赖 B,B 依赖 A)。此时 Spring 会抛出 BeanCurrentlyInCreationException,需通过以下方式解决:

  • 使用 @Lazy 延迟初始化(仅适用于构造器注入)。
  • 改为通过方法调用获取依赖(非构造器/@Autowired 注入)。

2. 依赖的 Bean 不存在

@DependsOn 声明的依赖 Bean 未在容器中注册,Spring 会抛出 NoSuchBeanDefinitionException。需确保依赖的 Bean 已通过 @Bean@Component@ConfigurationProperties 注册。

3. @Conditional 的协同

@DependsOn@Conditional 注解(如 @ConditionalOnProperty)可结合使用,实现条件化的依赖顺序控制。例如:仅当某个属性为 true 时,声明依赖关系。

4. 性能影响

@DependsOn 的处理在 Spring 启动时完成(调整 Bean 定义顺序),对运行时性能无影响。

5. 优先使用构造器注入

若依赖关系明确,优先通过构造器注入声明依赖(Spring 自动推导顺序),@DependsOn 仅作为补充(如隐式依赖场景)。


六、总结

@DependsOn 是 Spring 中显式控制 Bean 初始化顺序的核心注解,适用于依赖关系隐式或需要强制指定顺序的场景。其核心机制依赖 DependsOnAnnotationBeanPostProcessorBeanDefinitiondependsOn 属性,确保被依赖的 Bean 先于当前 Bean 初始化。理解其源码和使用场景,有助于开发者编写更健壮、可维护的 Spring 应用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值