携手创作,共同成长!这是我参与「掘金日新计划 · 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#doScan,AnnotationConfigUtils#processCommonDefinitionAnnotations(AnnotatedBeanDefinition, AnnotatedTypeMetadata)
创建bean时,也就是调用AbstractBeanFactory#doGetBean时,会获取这些被依赖的beanName,按照数组顺序,再调用AbstractBeanFactory#getBean(beanName)来优先创建被依赖的bean,从而达到控制依赖顺序。
除此之外,在创建bean时,还会调用AbstractBeanFactory#registerDisposableBeanIfNecessary来向Spring中注册带有销毁方法的bean,源码见DefaultSingletonBeanRegistry#registerDisposableBean,内部通过LinkedHashMap保存。key为bean名称。进程退出时,会逆序调用销毁方法。