共计 4123 个字符,预计需要花费 11 分钟才能阅读完成。
1. 为什么须要管制加载程序
springboot
听从约定大于配置的准则,极大水平的解决了配置繁琐的问题。在此基础上,又提供了 spi 机制,用 spring.factories
能够实现一个小组件的主动拆卸性能。
在个别业务场景,可能你不大关怀一个 bean 是如何被注册进 spring 容器的。只须要把须要注册进容器的 bean 申明为 @Component
即可,spring 会主动扫描到这个 Bean 实现初始化并加载到 spring 上下文容器。
而当你在我的项目启动时须要提前做一个业务的初始化工作时,或者你正在开发某个中间件须要实现主动拆卸时。你会申明本人的 Configuration 类,然而可能你面对的是好几个有相互依赖的 Bean。如果不加以控制,这时候可能会报找不到依赖的谬误。
然而你明明曾经把相干的 Bean 都注册进 spring 上下文了呀。这时候你须要通过一些伎俩来管制 springboot 中的 bean 加载程序。
2. 几个误区
在正式说如何管制加载程序之前,先说 2 个误区。
在标注了 @Configuration
的类中,写在后面的 @Bean 肯定会被先注册
这个不存在的,spring 在以前 xml 的时代,也不存在写在后面肯定会被先加载的逻辑。因为 xml 不是渐进的加载,而是全副 parse 好,再进行依赖剖析和注册。到了 springboot 中,只是省去了 xml 被 parse 成 spring 外部对象的这一过程,然而加载形式并没有大的扭转。
利用 @Order
这个标注能进行加载程序的管制
严格的说,不是所有的 Bean 都能够通过 @Order
这个标注进行程序的管制。你把 @Order
这个标注加在一般的办法上或者类上一点鸟用都没有。
那 @Order
能管制哪些 bean 的加载程序呢,咱们先看看官网的解释:
{@code @Order} defines the sort order for an annotated component. Since Spring 4.0, annotation-based ordering is supported for many kinds of components in Spring, even for collection injection where the order values of the target components are taken into account (either from their target class or from their {@code @Bean} method). While such order values may influence priorities at injection points, please be aware that they do not influence singleton startup order which is an orthogonal concern determined by dependency relationships and {@code @DependsOn} declarations (influencing a runtime-determined dependency graph).
最开始 @Order
注解用于切面的优先级指定;在 4.0 之后对它的性能进行了加强,反对汇合的注入时,指定汇合中 bean 的程序,并且特地指出了,它对于但实例的 bean 之间的程序,没有任何影响。
目前用的比拟多的有以下 3 点:
- 管制 AOP 的类的加载程序,也就是被
@Aspect
标注的类 - 管制
ApplicationListener
实现类的加载程序 - 管制
CommandLineRunner
实现类的加载程序
3. 如何管制
3.1@DependsOn
@DependsOn
注解能够用来管制 bean 的创立程序,该注解用于申明以后 bean 依赖于另外一个 bean。所依赖的 bean 会被容器确保在以后 bean 实例化之前被实例化。
示例:
@Configuration
public class BeanOrderConfiguration {
@Bean
@DependsOn("beanB")
public BeanA beanA(){System.out.println("bean A init");
return new BeanA();}
@Bean
public BeanB beanB(){System.out.println("bean B init");
return new BeanB();}
@Bean
@DependsOn({"beanD","beanE"})
public BeanC beanC(){System.out.println("bean C init");
return new BeanC();}
@Bean
@DependsOn("beanE")
public BeanD beanD(){System.out.println("bean D init");
return new BeanD();}
@Bean
public BeanE beanE(){System.out.println("bean E init");
return new BeanE();}
}
以上代码 bean 的加载程序为:
bean B init
bean A init
bean E init
bean D init
bean C init
@DependsOn
的应用:
- 间接或者间接标注在带有
@Component
注解的类下面; - 间接或者间接标注在带有
@Bean
注解的办法下面; - 应用
@DependsOn
注解到类层面仅仅在应用 component-scanning 形式时才无效,如果带有@DependsOn
注解的类通过 XML 形式应用,该注解会被疏忽,<bean depends-on="..."/>
这种形式会失效。
3.2 参数注入
在 @Bean
标注的办法上,如果你传入了参数,springboot 会主动会为这个参数在 spring 上下文里寻找这个类型的援用。并先初始化这个类的实例。
利用此个性,咱们也能够管制 bean 的加载程序。
示例:
@Bean
public BeanA beanA(BeanB demoB){System.out.println("bean A init");
return new BeanA();}
@Bean
public BeanB beanB(){System.out.println("bean B init");
return new BeanB();}
以上后果,beanB 先于 beanA 被初始化加载。
须要留神的是,springboot 会按类型去寻找。如果这个类型有多个实例被注册到 spring 上下文,那你就须要加上 @Qualifier("Bean 的名称")
来指定
3.3 利用 bean 的生命周期中的扩大点
在 spring 体系中,从容器到 Bean 实例化 & 初始化都是有生命周期的,并且提供了很多的扩大点,容许你在这些步骤时进行逻辑的扩大。
这些可扩大点的加载程序由 spring 本人管制,大多数是无奈进行干涉的。咱们能够利用这一点,扩大 spring 的扩大点。在相应的扩大点退出本人的业务初始化代码。素来达到程序的管制。
具体对于 spring 容器中大部分的可扩大点的剖析,之前曾经写了一篇文章具体介绍了:《Springboot 启动扩大点超具体总结,再也不怕面试官问了》。
3.4 @AutoConfigureOrder
这个注解用来指定配置文件的加载程序。然而在理论测试中发现,以下这样应用是不失效的:
@Configuration
@AutoConfigureOrder(2)
public class BeanOrderConfiguration1 {
@Bean
public BeanA beanA(){System.out.println("bean A init");
return new BeanA();}
}
@Configuration
@AutoConfigureOrder(1)
public class BeanOrderConfiguration2 {
@Bean
public BeanB beanB(){System.out.println("bean B init");
return new BeanB();}
}
无论你 2 个数字填多少,都不会扭转其加载程序后果。
那这个 @AutoConfigureOrder
到底是如何应用的呢。
通过测试发现,@AutoConfigureOrder
只能扭转内部依赖的 @Configuration
的程序。如何了解是内部依赖呢。
能被你工程外部 scan 到的包,都是外部的 Configuration,而 spring 引入内部的 Configuration,都是通过 spring 特有的 spi 文件:spring.factories
换句话说,@AutoConfigureOrder
能扭转 spring.factories
中的 @Configuration
的程序。
具体应用形式:
@Configuration
@AutoConfigureOrder(10)
public class BeanOrderConfiguration1 {
@Bean
public BeanA beanA(){System.out.println("bean A init");
return new BeanA();}
}
@Configuration
@AutoConfigureOrder(1)
public class BeanOrderConfiguration2 {
@Bean
public BeanB beanB(){System.out.println("bean B init");
return new BeanB();}
}
spring.factories
:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.example.demo.BeanOrderConfiguration1,\
com.example.demo.BeanOrderConfiguration2
4. 总结
其实在工作中,我置信很多人碰到过简单的依赖关系的 bean 加载,把这种不确定性交给 spring 去做,还不如咱们本人去管制,这样在浏览代码的时候,也能轻易看出 bean 之间的依赖先后顺序。
5. 分割作者
微信关注 jishuyuanren 获取更多技术干货