如何正确控制springboot中bean的加载顺序总结


1.为何须要控制加载顺序web

springboot听从约定大于配置的原则,极大程度的解决了配置繁琐的问题。在此基础上,又提供了spi机制,用spring.factories能够完成一个小组件的自动装配功能。面试

在通常业务场景,可能你不大关心一个bean是如何被注册进spring容器的。只须要把须要注册进容器的bean声明为@Component便可,spring会自动扫描到这个Bean完成初始化并加载到spring上下文容器。spring

而当你在项目启动时须要提早作一个业务的初始化工做时,或者你正在开发某个中间件须要完成自动装配时。你会声明本身的Configuration类,可是可能你面对的是好几个有互相依赖的Bean。若是不加以控制,这时候可能会报找不到依赖的错误。编程

可是你明明已经把相关的Bean都注册进spring上下文了呀。这时候你须要经过一些手段来控制springboot中的bean加载顺序。springboot

2.几个误区

在正式说如何控制加载顺序以前,先说2个误区。微信

在标注了@Configuration的类中,写在前面的@Bean必定会被先注册app

这个不存在的,spring在之前xml的时代,也不存在写在前面必定会被先加载的逻辑。由于xml不是渐进的加载,而是所有parse好,再进行依赖分析和注册。到了springboot中,只是省去了xml被parse成spring内部对象的这一过程,可是加载方式并无大的改变。学习

利用@Order这个标注能进行加载顺序的控制测试

严格的说,不是全部的Bean均可以经过@Order这个标注进行顺序的控制。你把@Order这个标注加在普通的方法上或者类上一点鸟用都没有。spa

@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实例化以前被实例化。

示例:

               

@Configurationpublic 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 initbean A initbean E initbean D initbean C init


@DependsOn的使用:

  • 直接或者间接标注在带有@Component注解的类上面;

  • 直接或者间接标注在带有@Bean注解的方法上面;

  • 使用@DependsOn注解到类层面仅仅在使用 component-scanning 方式时才有效,若是带有@DependsOn注解的类经过XML方式使用,该注解会被忽略,<bean depends-on="..."/>这种方式会生效。

3.2 参数注入

@Bean标注的方法上,若是你传入了参数,springboot会自动会为这个参数在spring上下文里寻找这个类型的引用。并先初始化这个类的实例。

利用此特性,咱们也能够控制bean的加载顺序。

示例:

               

@Beanpublic BeanA beanA(BeanB demoB){ System.out.println("bean A init"); return new BeanA();}
@Beanpublic 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之间的依赖前后顺序。

END


我是武哥,最后给你们 免费分享我写的 10 万字 Spring Boot 学习笔记(带完整目录)以及对应的源码 。这是我以前在 CSDN 开的一门课,因此笔记很是详细完整,我准备将资料分享出来给你们免费学习,相信你们看完必定会有所收获( 下面有下载方式 )。


能够看出,我当时备课很是详细,目录很是完整,读者能够手把手跟着笔记,结合源代码来学习。如今免费分享出来,有须要的读者能够下载学习,就在我公众号回复:笔记,就行。



若有文章对你有帮助,

在看转发是对我最大的支持



关注Java开发宝典

天天学习技术干货



点赞是最大的支持 

本文分享自微信公众号 - 武哥聊编程(eson_15)。
若有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一块儿分享。

相关文章
相关标签/搜索