当前位置: 首页 > 科技观察

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

时间:2023-03-14 19:28:54 科技观察

1.为什么需要控制加载顺序?Springboot遵循约定优于配置的原则,极大的解决了配置繁琐的问题。在此基础上提供了spi机制,用spring.factories可以完成一个小组件的自动装配功能。在一般的业务场景下,你可能不太关心一个bean是如何注册到spring容器中的。只需将需要注册到容器中的bean声明为@Component,spring会自动扫描bean完成初始化并加载到spring上下文容器中。而当你在项目启动的时候需要提前做一个业务初始化工作,或者你在开发某个中间件需要完成自动组装的时候。您将声明自己的Configuration类,但您可能会面临多个相互依赖的bean。如果不加以控制,此时可能会报找不到依赖的错误。但是您显然已经将所有相关bean注册到spring上下文中。这时候就需要通过一些手段来控制springboot中bean的加载顺序。2.几个误区在正式讲怎么控制加载顺序之前,先说2个误区。在@Configuration标注的类中,写在前面的@Bean肯定会先被注册。这不存在。在之前spring的xml时代,没有写在前面的@Bean必须先加载的逻辑。因为xml不是逐步加载的,而是全部解析,然后进行依赖分析和注册。在springboot中只是省略了xml被解析成spring内部对象的过程,但是加载方式并没有太大变化。@Order注解可以用来控制加载顺序。严格来说,并不是所有的bean都可以通过@Order注解来控制。你给普通的方法或者类加上@Order注解是没有用的。那@Order能控制哪些bean的加载顺序呢,我们先看看官方的解释:{@code@Order}definesthesortorderforanannotatedcomponent.SinceSpring4.0,annotation-basedorderingissupportedformanykindsofcomponentsinSpring,evenforcollectioninjectionwheretheordervaluesofthetargetcomponentsaretakenintoaccount(eitherfromtheirtargetclassorfromtheir{@code@Bean}method).Whilesuchordervaluesmayinfluenceprioritiesatinjectionpoints,请注意它们不会影响单例启动顺序,这是由依赖关系和{@code@DependsOn}声明确定的正交问题(影响运行时确定的依赖关系图)。最初,@Order注解用于指定切面的优先级;4.0之后,增强了其功能,支持按顺序注入collection,指定,并特别指出它对实例的bean之间的顺序没有影响。目前使用比较多的有以下三点:控制AOP类的加载顺序,即@Aspect标记的类控制ApplicationListener实现类的加载顺序控制CommandLineRunner实现类的加载顺序3.如何控制3.1@DependsOn@DependsOn注解可以用来控制bean的创建顺序。此注解用于声明当前bean依赖于另一个bean。依赖bean由容器保证在当前bean被实例化之前被实例化。示例:@ConfigurationpublicclassBeanOrderConfiguration{@Bean@DependsOn("beanB")publicBeanAbeanA(){System.out.println("beanAinit");returnnewBeanA();}@BeanpublicBeanBbeanB(){System.out.println("beanBinit");返回newBeanB();}@Bean@DependsOn({"beanD","beanE"})publicBeanCbeanC(){System.out.println("beanCinit");returnnewBeanC();}@Bean@DependsOn("beanE")publicBeanDbeanD(){System.out.println("beanDinit");returnnewBeanD();}@BeanpublicBeanEbeanE(){System.out.println("beanEinit");returnnewBeanE();}}以上代码bean的加载顺序为:beanBinitbeanAinitbeanEinitbeanDinitbeanCinit@DependsOn的使用:直接或间接在类上标注@Component注解;直接或间接在方法上标注@Bean注解;在class级别使用@DependsOn注解只有在使用component-scanning方法时才有效,如果带有@DependsOn注解的class被XML使用,注解将被忽略,将生效。3.2参数注入在@Bean标记的方法上,如果传入一个参数,springboot会自动在spring上下文中为这个参数寻找该类型的引用。并且先初始化这个类的一个实例。使用此功能,我们还可以控制加载bean的顺序。例子:@BeanpublicBeanAbeanA(BeanBdemoB){System.out.println("beanAinit");returnnewBeanA();}@BeanpublicBeanBeanBbeanB(){System.out.println("beanBinit");returnnewBeanB();}以上结果,beanB首先BeanA被初始化和加载。需要注意的是springboot会按类型查找。如果在springcontext中注册了多个该类型的实例,那么需要添加@Qualifier("Beanname")来指定3.3在bean的生命周期中使用扩展点在spring系统中,从容器到Bean实例initialization&initialization都有生命周期,并且提供了很多扩展点,让你在这些步骤中扩展逻辑。这些扩展点的加载顺序由spring自己控制,大部分是无法干预的。我们可以利用这一点来扩展spring的扩展点。在对应的扩展点添加自己的业务初始化代码。永远不要实现顺序控制。关于spring容器中大部分扩展点的分析,我写过一篇文章详细介绍过:《Springboot启动扩展点超详细总结,再也不怕面试官问了》。3.4@AutoConfigureOrder该注解用于指定配置文件的加载顺序。但在实际测试中,发现如下用法无效:@Configuration@AutoConfigureOrder(2)publicclassBeanOrderConfiguration1{@BeanpublicBeanAbeanA(){System.out.println("beanAinit");returnnewBeanA();}}@Configuration@AutoConfigureOrder(1)publicclassBeanOrderConfiguration2{@BeanpublicBeanBbeanB(){System.out.println("beanBinit");returnnewBeanB();}}无论你填了多少2个数字,它的加载顺序结果都不会改变。那么这个@AutoConfigureOrder是怎么使用的呢?经过测试发现@AutoConfigureOrder只能改变外部依赖@Configuration的顺序。如何理解它是一个外部依赖。你项目内部能扫描到的包都是内部的Configuration,而spring是通过spring特有的spi文件引入外部的Configuration:spring.factories,也就是说@AutoConfigureOrder可以改变@Configuration在spring.factories中的顺序。具体用法:@Configuration@AutoConfigureOrder(10)publicclassBeanOrderConfiguration1{@BeanpublicBeanAbeanA(){System.out.println("beanAinit");returnnewBeanA();}}@Configuration@AutoConfigureOrder(1)publicclassBeanOrderConfiguration2{@BeanpublicBeanBbeanB(){System.out.println("beanBinit");returnnewBeanB();}}spring.factories:org.springframework.boot.autoconfigure.EnableAutoConfiguration=\com.example.demo.BeanOrderConfiguration1,\com.example.demo.BeanOrderConfiguration24。在工作中,相信很多人都遇到过复杂依赖的bean加载。把这种不确定性交给spring,比自己去控制要好,这样在阅读代码的时候,我们很容易看出bean之间的依赖顺序。

最新推荐
猜你喜欢