当前位置: 首页 > 编程笔记 >

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

公羊兴文
2023-03-14
本文向大家介绍如何正确控制springboot中bean的加载顺序小结篇,包括了如何正确控制springboot中bean的加载顺序小结篇的使用技巧和注意事项,需要的朋友参考一下

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点:

@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之间的依赖先后顺序。

到此这篇关于如何正确控制springboot中bean的加载顺序总结的文章就介绍到这了,更多相关springboot中bean的加载顺序内容请搜索小牛知识库以前的文章或继续浏览下面的相关文章希望大家以后多多支持小牛知识库!

 类似资料:
  • 3.1 顺序控制结构 程序是一个语句序列,执行程序就是按特定的次序执行程序中的语句。程序中执行点的 变迁称为控制流程,当执行到程序中的某一条语句时,也说控制转到了该语句。由于复杂问 题的解法可能涉及复杂的执行次序,因此编程语言必须提供表达复杂控制流程的手段,称为 编程语言的控制结构。 程序的控制流程可以用流程图(flowchart)来形象地表示。流程图采用标准化的图形符 号来描述程序的执行步骤,是

  • 我有一个带有Web模块和ejb模块的耳朵文件(仅用于消息驱动的bean)。ejb 模块依赖于 Web 模块及其类。我需要先加载 Web 模块,然后再加载 ejb 模块。但是,自由总是首先加载 ejb 模块,从而导致 如何控制模块在同一ear文件中的加载顺序?在传统的webshpere上有一个名为“起始重量”的选项。无论哪个模块的值最低,都会优先加载。因此该应用程序在传统Websphere上运行良好

  • 问题内容: 好的,我发现必须发布一个新问题才能找到答案,这真是太荒谬了,可惜我在这里。让下一个任性的灵魂尽可能解决这个问题,让它尽可能简单。 要使ajax表单正常工作,我需要什么最新的脚本?到目前为止,我有; 我看过的页面指出我也需要jquery.unobtrusive-ajax.js,但这只是给我一个错误。是否加载了一些不兼容的版本? 过去,我已经为MVC3等进行过多次处理,但是很愚蠢的是,我似

  • 所以我的控制器的结构可能是造成这种情况的原因。在这里您可以看到父控制器和子控制器,但重要的部分在这里: 父控制器 子控制器 如果刷新页面,或者从应用程序外部导航到页面(任何导致页面完全加载的内容),版本就会工作。导航到此时,为空,此方法失败。 因此,当“深度链接”或刷新时,以及当进行内部导航时,应用程序加载控制器的顺序是不同的。如何从具有父子关系的角度控制器获得一致的负载行为?

  • 问题内容: 好的,所以我一直在尝试使用以下代码加载BufferedImage: 但是,这在运行时给了我一个类型转换错误,那么如何正确加载BufferedImage? 问题答案: 使用来代替:

  • 问题内容: 我有3个相互依赖的xsd文件来构建我的元素定义。每个xsd文件都有其自己的名称空间。当我使用JAXB xjc生成类时,得到3个相应的包。到目前为止,一切都很好。 当我想使用解组器进行架构验证时,就会出现我的问题。为了避免不得不读取xsd文件,我从被解组的相关类中动态生成了模式。但是,由于该类依赖于其他2个包中的对象,因此除非我指定所有3个包,否则它无法生成架构。这已经不是一个非常实用的