指定Bean加载顺序的能力

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

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

但是,如果加载Bean的过程中部分Bean和Bean之间存在依赖关系,也就是说Bean A的加载需要等待Bean B加载完成之后才能进行;或者你正在开发某个中间件需要完成自动装配时,你会声明自己的Configuration类,但是可能你面对的是好几个有互相依赖的Bean,如果不加以控制,这时候可能会报找不到依赖的错误。

而Spring框架在没有明确指定加载顺序的情况下是无法按照业务逻辑预期的顺序进行Bean加载,所以需要Spring框架提供能让开发人员显示地指定Bean加载顺序的能力。

几个误区

在正式说如何控制加载顺序之前,先说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实现类的加载顺序

使用详情请看后文

如何控制

@Conditional 条件注解家族

  • @ConditionalOnClass:当类路径下存在指定的类时,配置类才会生效。
@Configuration
// 当类路径下存在指定的类时,配置类才会生效。
@ConditionalOnClass(name = "com.example.SomeClass")
public class MyConfiguration {// ...
}
  • @ConditionalOnMissingClass:当类路径下不存在指定的类时,配置类才会生效。
  • @ConditionalOnBean:当容器中存在指定的Bean时,配置类才会生效。
  • @ConditionalOnMissingBean:当容器中不存在指定的Bean时,配置类才会生效。

@DependsOn

@DependsOn注解可以用来控制bean的创建顺序,该注解用于声明当前bean依赖于另外一个bean。所依赖的bean会被容器确保在当前bean实例化之前被实例化。

@DependsOn的使用:

  • 直接或者间接标注在带有@Component注解的类上面;
  • 直接或者间接标注在带有@Bean注解的方法上面;
  • 使用@DependsOn注解到类层面仅仅在使用 component-scanning 方式时才有效,如果带有@DependsOn注解的类通过XML方式使用,该注解会被忽略,<bean depends-on="..."/>这种方式会生效。

示例:

@Configuration
public class BeanOrderConfiguration {@Bean@DependsOn("beanB")public BeanA beanA(){System.out.println("bean A init");return new BeanA();}@Beanpublic 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();}@Beanpublic 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

参数注入

@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的名称")来指定

利用bean的生命周期中的扩展点

在spring体系中,从容器到Bean实例化&初始化都是有生命周期的,并且提供了很多的扩展点,允许在这些步骤时进行逻辑的扩展。

这些可扩展点的加载顺序由spring自己控制,大多数是无法进行干预的。可以利用这一点,扩展spring的扩展点。在相应的扩展点加入自己的业务初始化代码。从来达到顺序的控制。

具体关于spring容器中大部分的可扩展点的分析,之前已经写了一篇文章详细介绍了

实现Ordered/PriorityOrdered接口/注解

在Spring中提供了如下的方法来进行Bean加载顺序的控制:

  • 实现Ordered/PriorityOrdered接口,重写order方法
  • 使用@Order/@Priority注解,@Order注解可以用于方法级别,而@Priority注解则不行;

针对自定义的Bean而言,上述的方式都可以实现Bean加载顺序的控制。无论是实现接口的方式还是使用注解的方式,值设置的越小则优先级越高,而通过实现PriorityOrdered接口或者使用@Priority注解的Bean时其加载优先级会高于实现Ordered接口或者使用@Order注解的Bean。

需要注意的是,使用上述方式只会改变实现同一接口Bean加载到集合(比如List、Set等)中的顺序(或者说优先级),但是这种方式并不会影响到Spring应用上下文启动时不同Bean的初始化顺序(startup order)。

  • 错误案例:以下案例代码是无法指定配置顺序的
@Component
@Order(1)
public class BeanA {// BeanA的定义
}@Component
@Order(2)
public class BeanB {// BeanB的定义
}
  • 正确使用案例:

首先定义两个 Bean 实现同一个接口,并添加上@Order注解。

public interface IBean {
}@Order(2)
@Component
public class AnoBean1 implements IBean {private String name = "ano order bean 1";public AnoBean1() {System.out.println(name);}
}@Order(1)
@Component
public class AnoBean2 implements IBean {private String name = "ano order bean 2";public AnoBean2() {System.out.println(name);}
}

然后在一个测试 bean 中,注入IBean的列表,我们需要测试这个列表中的 Bean 的顺序是否和定义的@Order规则一致

@Component
public class AnoTestBean {public AnoTestBean(List<IBean> anoBeanList) {for (IBean bean : anoBeanList) {System.out.println("in ano testBean: " + bean.getClass().getName());}}
}

@AutoConfigureOrder

这个注解用来指定配置文件的加载顺序。但是在实际测试中发现,以下这样使用是不生效的:

@Configuration
@AutoConfigureOrder(2)
public class BeanOrderConfiguration1 {@Beanpublic BeanA beanA(){System.out.println("bean A init");return new BeanA();}
}@Configuration
@AutoConfigureOrder(1)
public class BeanOrderConfiguration2 {@Beanpublic BeanB beanB(){System.out.println("bean B init");return new BeanB();}
}

无论你2个数字填多少,都不会改变其加载顺序结果。那这个@AutoConfigureOrder到底是如何使用的呢?

@AutoConfigureOrder适用于外部依赖的包中 AutoConfig 的顺序,而不能用来指定本包内的顺序。能被你工程内部scan到的包,都是内部的Configuration,而spring引入外部的Configuration,都是通过spring特有的spi文件:spring.factories

换句话说,@AutoConfigureOrder能改变spring.factories中的@Configuration的顺序。

具体使用方式:

@Configuration
@AutoConfigureOrder(10)
public class BeanOrderConfiguration1 {@Beanpublic BeanA beanA(){System.out.println("bean A init");return new BeanA();}
}@Configuration
@AutoConfigureOrder(1)
public class BeanOrderConfiguration2 {@Beanpublic 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

总结

其实在工作中,我相信很多人碰到过复杂的依赖关系的bean加载,把这种不确定性交给spring去做,还不如我们自己去控制,这样在阅读代码的时候 ,也能轻易看出bean之间的依赖先后顺序。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/890889.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

后端开发如何高效使用 Apifox?

Apifox 是一个 API 协作开发平台&#xff0c;后端、前端、测试都可以使用 Apifox 来提升团队的工作效率。对于后端开发者而言&#xff0c;Apifox 的核心功能主要包括四个模块&#xff1a;调用 API、定义 API、开发与调试 API 以及生成 API 文档。本文将详细介绍后端开发人员如何…

flask后端开发(11):User模型创建+注册页面模板渲染

目录 一、数据库创建和配置信息1.新建数据库2.数据库配置信息3.User表4.ORM迁移 二、注册页面模板渲染1.导入静态文件2.蓝图注册路由 一、数据库创建和配置信息 1.新建数据库 终端中 CREATE DATABASE zhiliaooa DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;2…

极客说|微软新模型:Phi-4 来了

作者&#xff1a;魏新宇 - 微软 AI 全球黑带高级技术专家 「极客说」 是一档专注 AI 时代开发者分享的专栏&#xff0c;我们邀请来自微软以及技术社区专家&#xff0c;带来最前沿的技术干货与实践经验。在这里&#xff0c;您将看到深度教程、最佳实践和创新解决方案。关注「极客…

redis相关数据类型介绍

当然&#xff0c;Redis 作为一个高性能的键值存储系统&#xff0c;提供了多种数据类型来支持不同的应用场景。 1. String&#xff08;字符串&#xff09; • 定义&#xff1a;Redis 最基本的数据类型&#xff0c;用于存储字符串值。 • 操作&#xff1a;SET、GET、INCR、DECR、…

arthas查看拼接好参数的sql, redis, es完整可直接执行的命令

arthas查看拼接好参数的sql, redis, es完整可直接执行的命令 arthas查看sql可执行命令arthas查看redis可执行命令arthas查看es可执行命令相关链接 经常修bug的时候, 拿不到能够执行的命令, 真是太难受了 arthas查看sql可执行命令 # mybatis plus (参数和sql分离了) watch org.…

OpenHarmony怎么修改DPI密度值?RK3566鸿蒙开发板演示

本文介绍在开源鸿蒙OpenHarmony系统下&#xff0c;修改DPI密度值的方法&#xff0c;触觉智能Purple Pi OH鸿蒙开发板演示&#xff0c;搭载了瑞芯微RK3566四核处理器&#xff0c;Laval鸿蒙社区推荐开发板&#xff0c;已适配全新开源鸿蒙OpenHarmony5.0 Release系统&#xff0c;适…

电子应用设计方案74:智能家庭对讲系统设计

智能家庭对讲系统设计 一、引言 智能家庭对讲系统作为智能家居的重要组成部分&#xff0c;为家庭成员之间以及与访客的沟通提供了便捷、高效的方式。本设计方案旨在打造一个功能强大、稳定可靠、操作简便且具有良好扩展性的智能家庭对讲系统。 二、系统概述 1. 系统目标 - 实…

《鸿蒙HarmonyOS应用开发从入门到精通(第2版)》学习笔记——HarmonyOS技术理念

1.2 技术理念 在万物智联时代重要机遇期&#xff0c;HarmonyOS结合移动生态发展的趋势&#xff0c;提出了三大技术理念&#xff08;如下图3-1所示&#xff09;&#xff1a;一次开发&#xff0c;多端部署&#xff1b;可分可合&#xff0c;自由流转&#xff1b;统一生态&#xf…

《医药养生保健报》是正规报刊吗?如何在数据库搜索报刊信息?

在数据库检索报刊的正规性&#xff0c;可以说是论文发表环节中一个重中之重的环节。文章能否被数据库正常收录&#xff0c;很大程度上会影响到毕业、评职称的审核结果。 目前主流认可的三大数据库分别为中国知网、万方数据以及维普资讯。接下来就让我们以《医药养生保健报》为例…

OSI 七层模型 | TCP/IP 四层模型

注&#xff1a;本文为 “OSI 七层模型 | TCP/IP 四层模型” 相关文章合辑。 未整理去重。 OSI 参考模型&#xff08;七层模型&#xff09; BeretSEC 于 2020-04-02 15:54:37 发布 OSI 的概念 七层模型&#xff0c;亦称 OSI&#xff08;Open System Interconnection&#xf…

基于 Python Django 的农产品销售系统的研究与实现

大家好&#xff0c;我是stormjun&#xff0c;今天为大家带来的是基于 Python Django 的农产品销售系统的研究与实现。该系统采用 Python 语言 开发&#xff0c;MySql 作为数据库&#xff0c;系统功能完善 &#xff0c;实用性强 &#xff0c;可供大学生实战项目参考使用。 博主介…

uniapp实现APP、小程序与webview页面间通讯

需求&#xff1a; 1、需要在Uniapp开发的APP或小程序页面嵌入一个H5网页&#xff0c;需要拿到H5给APP传递的数据。 2、并且这个H5是使用vuevant开发的。&#xff08;其实跟使用uniapp开发H5一样&#xff09; 实现步骤&#xff1a; 1、首先需要兼容多端和App端&#xff0c;因…

FreeSWITCH 简单图形化界面38 - 使用uniapp中使用JsSIP进行音视频呼叫

FreeSWITCH 简单图形化界面38 - 在uniapp中使用JsSIP进行音视频呼叫 0、测试环境1、学习uniapp2、测试代码main.jsutils/render.jsstore/data.jspages/index/index.vuepages.json 3、效果4、难点 0、测试环境 http://myfs.f3322.net:8020/ 用户名&#xff1a;admin&#xff0c…

【蓝桥杯——物联网设计与开发】拓展模块4 - 脉冲模块

目录 一、脉冲模块 &#xff08;1&#xff09;资源介绍 &#x1f505;原理图 &#x1f505;采集原理 &#xff08;2&#xff09;STM32CubeMX 软件配置 &#xff08;3&#xff09;代码编写 &#xff08;4&#xff09;实验现象 二、脉冲模块接口函数封装 三、踩坑日记 &a…

嵌入式硬件杂谈(八)电源的“纹波”到底是什么?

纹波的引入&#xff1a;在我们嵌入式设备中&#xff0c;很多时候电路电源的纹波很敏感&#xff0c;纹波太大会导致系统不工作&#xff0c;因此设计一个纹波很小的电路就是我们的需求了。 电路的纹波是什么&#xff1f; 纹波&#xff08;Ripple&#xff09;是指电源输出中叠加在…

Linux系统之stat命令的基本使用

Linux系统之stat命令的基本使用 一、stat命令 介绍二、stat命令帮助2.1 查询帮助信息2.2 stat命令的帮助解释 三、stat命令的基本使用3.1 查询文件信息3.2 查看文件系统状态3.3 使用格式化输出3.4 以简洁形式打印信息 四、注意事项 一、stat命令 介绍 stat 命令用于显示文件或文…

uniapp开发微信小程序实现获取“我的位置”

1. 创建GetLocation项目 使用HBuilder X创建一个项目GetLocation,使用Vue3。 2. 在腾讯地图开放平台中创建应用 要获取位置,在小程序中需要使用腾讯地图或是高德地图。下面以腾讯地图为例。 (1)打开腾讯地图开放平台官方网址:腾讯位置服务 - 立足生态,连接未来 (2)注册…

基于NodeMCU的物联网空调控制系统设计

最终效果 基于NodeMCU的物联网空调控制系统设计 项目介绍 该项目是“物联网实验室监测控制系统设计&#xff08;仿智能家居&#xff09;”项目中的“家电控制设计”中的“空调控制”子项目&#xff0c;最前者还包括“物联网设计”、“环境监测设计”、“门禁系统设计计”和“小…

easegen将教材批量生成可控ppt课件方案设计

之前客户提出过一个需求&#xff0c;就是希望可以将一本教材&#xff0c;快速的转换为教学ppt&#xff0c;虽然通过人工程序脚本的方式&#xff0c;已经实现了该功能&#xff0c;但是因为没有做到通用&#xff0c;每次都需要修改脚本&#xff0c;无法让客户自行完成所有流程&am…

从安全角度看 SEH 和 VEH

从安全角度看 SEH 和 VEH 异常处理程序是处理程序中不可预见的错误的基本方法之一 https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/exceptions/ SEH——结构化异常处理程序 就其工作方式而言&#xff0c;异常处理程序与其他处理程序相比相当基础&#xff0…