认识一下@DependsOn

141 阅读3分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第9天,点击查看活动详情

一、定义

@DependsOn注解可以定义在类和方法上,意思是我这个组件要依赖于另一个组件,也就是说被依赖的组件会比该组件先注册到IOC容器中;该注解的属性是一个字符串数组,数组的元素是每个依赖的bean的名称。

//可以作用在方法和类上。
//当作用在类上时,通常会与@Component及其衍生注解等注解配合使用。
//当作用在方法上时,通常会与@Bean注解配合使用。
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DependsOn {
        //要依赖的bean id,是个数组,也就是说可以依赖多个bean。
        //效果是该注解作用的bean会比value设置的依赖bean晚实例化到容器中。
    String[] value() default {};
}

二、使用

主要用于指定当前bean所依赖的beans。任何被指定依赖的bean都由Spring保证在当前bean之前创建。在少数情况下,bean不是通过属性构造函数参数显式依赖于另一个bean,但却需要要求另一个bean优先完成初始化,则可以使用@DependsOn这个注解。

使用场景: 需要用到[观察者模式]的情况下通常都需要用到该注解,观察者模式有三要输,观察者、事件源、事件,机制是观察者会监听数据源的某些时间,当事件源触发该事件后,观察者就会知道进行相应措施。

1、配合@bean注解使用

EventSource类需要在eventListener加载完成后加载。

@Bean 
@DependsOn(value = {"eventListener"}) 
public EventSource eventSource(){ 
    return new EventSource(); 
}

2、与@Component配合使用
@Component
@DependsOn(value = {"eventTListener"})
public class EventSource {
    public EventSource(){
        System.out.println("事件源创建");
    }
}

spring默认扫描包时会根据文件在文件夹的位置先后顺序扫描加载,而EventSource 文件位置在EventTListener前面,所以会先加载EventSource 事件源组件;那么如果需要先加载监听器然后创建事件源,可以在事件源EventSource上依赖监听器@DependsOn(value = {"eventTListener"})

@DependsOn既可以指定初始化依赖顺序,也可以指定bean相应的销毁执行顺序(仅在单例bean的情况下);简单描述就是@DependsOn可以控制bean的创建、初始化(InitializingBean)、销毁方法执行顺序

三、 @DependsOn注解的实现原理

Spring在启动时扫描到一个bean,会封装成一个BeanDefinition,如果是AnnotatedBeanDefinition则解析类上的注解信息,发现@DependsOn注解,则读取value值,调用BeanDefinition#setDependsOn保存。 源码见ClassPathBeanDefinitionScanner#doScanAnnotationConfigUtils#processCommonDefinitionAnnotations(AnnotatedBeanDefinition, AnnotatedTypeMetadata)

创建bean时,也就是调用AbstractBeanFactory#doGetBean时,会获取这些被依赖的beanName,按照数组顺序,再调用AbstractBeanFactory#getBean(beanName)来优先创建被依赖的bean,从而达到控制依赖顺序。

除此之外,在创建bean时,还会调用AbstractBeanFactory#registerDisposableBeanIfNecessary来向Spring中注册带有销毁方法的bean,源码见DefaultSingletonBeanRegistry#registerDisposableBean,内部通过LinkedHashMap保存。key为bean名称。进程退出时,会逆序调用销毁方法