Spring Boot的自动装配中的@ConditionalOnBean条件装配注解在Spring启动过程中,是如何保证处理顺序靠后的

news2024/11/29 13:38:33
前言

        为什么Spring Boot条件注解那么多,而标题中是@ConditionalOnBean呢?

        因为,相比之下我们用的比较多的条件装配注解也就是@ConditionalOnClass、@ConditionalOnBean了,而@ConditionalOnClass对顺序并不敏感(说白了就是判断类加载器是否可以在其路径下加载到Class,所以和Spring的处理顺序没啥关系),但是@ConditionalOnBean就不同了,如果顺序无法保证,那么自动装配中的@ConditionalOnBean就可能会失效。

        还有一点需要强调一下,正如Spring Boot官方建议的那样,请在自动装配类中使用条件装配注解,不要在自己定义普通配置类(普通配置类指的是我们自定义的@Configuration配置类)中使用,在普通配置类中使用条件装配注解,要求程序员对配置类加载的顺序进行严格控制(显示的控制加载顺序)才可以百分百生效,尤其是@ConditionalOnBean这种对顺序敏感的注解;如果我们没有控制顺序,就可能导致在IDE中生效,但是到了线上就不生效了,因为你光靠ClassLoader的加载顺序是不靠谱的,在不同的操作系统环境下,class文件的加载顺序存在不确定性,文件是由文件系统管理,不同的文件管理系统有不同的机制。

        所以请切记,条件装配注解并不是无敌的,也是需要考虑使用场景的,不能随便的滥用,因此尽量在自动装配配置类中使用条件装配注解!!!

@ConditionalOnBean判断的是容器中是否存在BD,而不是判断的容器中是否存在Bean对象,这一点请注意。

为什么要保证顺序

        我们举个例子来说一下@ConditionalOnBean注解处理顺序的重要性。

例子中是@ConditionalOnMissingBean注解,但是和@ConditionalOnBean注解的逻辑基本一致的。下面代码表达的意思就是,我们定义的普通配置类中,对RedisTemplete进行了个性化设置,此时我们肯定希望容器中只存在我们自定义的RedisTemplate;而Spring Boot的自动装配的条件装配机制也确实满足了我们的这种需求;

@Configuration
public class RedisConfig {//这是我们自定义的普通配置类
    @Bean
    public RedisTemplate redisTemplate() {
        RedisTemplate<Object, Object> template = new RedisTemplate<>();	
        //巴拉巴拉一大堆个性化的逻辑
		//template.setXXX();
        return template;
    }
}

public class RedisAutoConfiguration {//这是Spring Boot自动装配的配置类

	@Bean
	@ConditionalOnMissingBean(name = "redisTemplate")
	@ConditionalOnSingleCandidate(RedisConnectionFactory.class)
	public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
		RedisTemplate<Object, Object> template = new RedisTemplate<>();
		template.setConnectionFactory(redisConnectionFactory);
		return template;
	}

	@Bean
	@ConditionalOnMissingBean
	@ConditionalOnSingleCandidate(RedisConnectionFactory.class)
	public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
		StringRedisTemplate template = new StringRedisTemplate();
		template.setConnectionFactory(redisConnectionFactory);
		return template;
	}

}

我们假设一下:如果RedisAutoConfiguration自动装配配置类在我们自定义的RedisConfig普通配置类前面执行了,那就会导致当时的容器中不存在名字为redisTemplate的bean对象,当处理我们自定义的RedisConfig配置类的时候,就会报错,因为存在同名的bean了。

总而言之,对于@ConditionalOnBean注解来说,顺序很重要,是必须要保证的。

顺序是如何保证的

        关于原理其实还是挺多内容的,因为需要足够清楚Spring的ConfigurationClassPostProcessor以及Spring Boot的AutoConfigurationImportSelector,这样才能彻底掌握原理中的细节内容。考虑到篇幅问题,我们还是以关键逻辑为主,弄清楚@ConditionalOnBean顺序是怎么保证的即可。

        首先我们说几点比较关键的基础知识。

  • @EnableAutoConfiguration:主要作用是开启Spring Boot的自动装配,属于Spring Boot注解;
  • @Import:主要作用是向容器中导入BeanDefinition,导入的BD直接当做ConfigurationClass来处理;
  • ImportSelector:是一个接口,主要作用是向容器中导入BD;
  • DeferredImportSelector:是一个继承了ImportSelector的接口,除了继承的能力外,还额外增加了延迟导入的能力(当然这个延迟导入借助的是Spring对ConfigurationClass的处理流程来实现的,并不是DeferredImportSelector自身具有延迟导入能力的方法);
  • AutoConfigurationImportSelector:是一个实现了DeferredImportSelector接口的实现类,自动装配的核心逻辑主要就在这里;

        可能对不熟悉Spring的人来说,没办法将上述几点串联起来。所以,我们在花费一些篇幅来详细的解释下上面几个点。

  1. 我们都知道一般会在启动类上存在@SpringBootApplication注解,而@SpringBootApplication注解上就标注了@EnableAutoConfiguration。
  2. @EnableAutoConfiguration注解上面配置了@Import(AutoConfigurationImportSelector.class)。
  3. AutoConfigurationImportSelector实现了DeferredImportSelector。
  4. DeferredImportSelector继承了ImportSelector。
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
		@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
	......此处省略很多无关内容
}

@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
	......此处省略一些无关内容
}

public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
	......此处省略很多无关内容
}

public interface DeferredImportSelector extends ImportSelector {
	......此处省略一些无关内容
}

        通过代码示例,我们可以清楚的看到,在编码层面,这几个注解、接口以及实现类已经串起来了。

        下面画个流程简图,来说明一下Spring是如何保证顺序的。

绿色为相关度较高的关键代码逻辑;红色条件装配的判断节点; 

        我们再用文字把流程中的关键点整理一下:

  1. ConfigurationClass的处理分2步;第一步:处理ConfigurationClass的注解信息并保存下来;第二步:对保存下来的信息进行处理,创建BD并注册到容器中。
  2. 在第一步中,普通配置类先进行处理,遇到自动装配的AutoConfigurationImportSelector(属于DeferredImportSelector接口类型)会先保存起来,等到普通配置类处理完成后,再进行统一的DeferredImportSelector接口类型的处理;在处理过程中导入的配置类都会放在LinkedHashMap类型的configurationClasses集合中,这样就可以利用默认情况下LinkedHashMap按照插入顺序遍历的特性,来控制普通配置类和自动装配配置类顺序。
  3. 在第二步中,已经得到了带有顺序的configurationClasses集合,普通配置类在前,自动装配配置类在后;这样在循环处理的时候,就是先处理的普通配置类,普通配置类处理完成后,已经将相应的BD注册到了容器中;而在处理自动装配配置类的时候,通过条件装配的判断,可以准确的判断出当前容器中是否存在对应的BD,这样就保证了自动装配功能的正常。
结语

        虽然@ConditionalOnBean是Spring Boot的注解,但是我们通过流程图发现,大部分核心的代码逻辑都是属于Spring的。因此,@ConditionalOnBean条件装配的顺序就是Spring的DeferredImportSelector接口延迟处理机制来保证的。

        对于熟悉Spring源码的人来说,可能看这篇文章很容易;如果不太熟悉,可以了解一下ConfigurationClassPostProcessor这个类,因为对于注解驱动(使用xml配置文件的项目越来越少了)的项目来说,这个类真的是太重要了。这个类处理的是BeanDefinition阶段,因此可以不用对Bean注册阶段有过多的了解。了解了ConfigurationClassPostProcessor这个类,那么读懂条件装配的原理,并且掌握代码逻辑细节,那肯定是轻轻松松儿的。

        下面是我整理的关于 ConfigurationClassPostProcessor的主要逻辑流程图,也附上,有需要的可以看看。

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

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

相关文章

蓝牙智能音箱采用哪些音频功放芯片

目前&#xff0c;无线蓝牙智能音箱越来越受广大用户的欢迎&#xff1b;比如点播歌曲、上网购物&#xff0c;或是了解天气预报&#xff0c;它也可以对智能家居设备进行控制&#xff0c;比如打开窗帘、设置冰箱温度、提前让热水器升温等。 而功放芯片是音箱中至关重要的组成部分…

分布式事务-TCC案例分析流程图

防止cancel方法在最后执行出现问题&#xff0c;用户收到提示已经退款成功但是由于cancel过慢或者出现问题&#xff08;虽然最后会重试成功但是用户体验很差&#xff09;&#xff0c;可以做以下的业务sql模型优化(增加一个冻结金额)。

Arcgis克里金插值报错:ERROR 010079: 无法估算半变异函数。 执行(Kriging)失败。

Arcgis克里金插值报错&#xff1a;ERROR 010079: 无法估算半变异函数。 执行(Kriging)失败。 问题描述&#xff1a; 原因&#xff1a; shape文件的问题&#xff0c;此图可以看出&#xff0c;待插值的点有好几个都超出了地理范围之外&#xff0c;这个不知道是坐标系配准的问…

Tensorflow2 GPU 安装方法

一、Tensorflow2 GPU 安装方法 1. 首先安装Anaconda3环境2. 在Anaconda Prompt 中安装tensorflow23. 验证GPU是否可以使用 1. 首先安装Anaconda3环境 https://www.anaconda.com/ 2. 在Anaconda Prompt 中安装tensorflow2 conda update conda conda create -n tensorflow pyt…

基于Java的社区管理系统设计与实现(源码+lw+部署文档+讲解等)

文章目录 前言功能介绍&#xff1a;具体实现截图论文参考详细视频演示为什么选择我自己的网站自己的小程序&#xff08;小蔡coding&#xff09;有保障的售后福利 代码参考源码获取 前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计划导…

【算法导论】快速排序

文章目录 1. 快速排序的描述 1.1基本描述1.2 PARTITOION函数1.3 快速排序C完整代码 2. 快速排序的性能2.1 最坏时间复杂度2.2 平均时间复杂度 1. 快速排序的描述 1.1基本描述 快速排序是一种时间复杂度为 O(n^2) 的排序算法。虽然最坏情况时间复杂度很差&#xff0c;但他的平…

开学教师自我介绍模版:打造个人品牌,轻松赢得学生喜爱

这里有一篇2000字左右的教师自我介绍长文&#xff1a; 亲爱的同学们&#xff0c;我是你们的XX老师。开学第一天&#xff0c;我非常高兴能在这里与大家见面。作为一名教师&#xff0c;我的职责是把知识教给你们&#xff0c;指导你们成长。同时&#xff0c;我也希望通过这次自我…

CocosCreator3.8研究笔记(二十三)CocosCreator 动画系统-动画编辑器相关功能面板说明

国庆假期&#xff0c;闲着没事&#xff0c;在家研究技术~ 上一篇&#xff0c;我们介绍了动画剪辑、动画组件以及基本的使用流程&#xff0c;感兴趣的朋友可以前往阅读&#xff1a; CocosCreator 动画系统-动画剪辑和动画组件介绍。 今天&#xff0c;主要介绍动画编辑器相关功能…

CocosCreator3.8研究笔记(二十二)CocosCreator 动画系统-动画剪辑和动画组件介绍

国庆假期&#xff0c;闲着没事&#xff0c;在家研究技术~ 大家都知道在Cocos Creator3.x 的版本的动画编辑器中&#xff0c;可以实现不用写一行代码就能实现各种动态效果。 Cocos Creator动画编辑器中主要实现关键帧动画&#xff0c;不仅支持位移、旋转、缩放、帧动画&#xff…

ASUS华硕天选4笔记本电脑FX507VV原厂Windows11系统

下载链接&#xff1a;https://pan.baidu.com/s/1W9tedHI3iFjaHju5eLkQ6g?pwd8dl2 系统自带所有驱动、出厂主题壁纸LOGO、Office办公软件、华硕电脑管家、奥创控制中心等预装程序 由于时间关系,绝大部分资料没有上传&#xff0c;不是想要的型号&#xff0c;请联系客服获取。

Spring的注解开发-Bean基本注解开发

Bean基本注解开发 Spring除了xml配置文件进行配置之外&#xff0c;还可以使用注解方式进行配置&#xff0c;注解方式慢慢成为xml配置的替代方案。我们有了xml开发的经验&#xff0c;学习注解开发就会方便很多&#xff0c;注解开发更加快捷方便。Spring提供的注解有三个版本 2.…

【文献阅读】Pocket2Mol : 基于3D蛋白质口袋的高效分子采样 + CrossDocked数据集说明

Pocket2Mol: Efficient Molecular Sampling Based on 3D Protein Pockets code&#xff1a; GitHub - pengxingang/Pocket2Mol: Pocket2Mol: Efficient Molecular Sampling Based on 3D Protein Pockets 所用数据集 与“A 3D Generative Model for Structure-Based Drug Desi…

【网络原理】初始网络,了解概念

文章目录 1. 网络通信1.1 局域网LAN1.2 广域网WAN 2. 基础概念2.1 IP2.2 端口号 3. 认识协议4. 五元组5. 协议分层5.1 分层的作用5.2 OSI七层模型5.3 TCP/IP五层&#xff08;四层&#xff09;模型 6. 封装和分用 1. 网络通信 计算机与计算机之间是互相独立&#xff0c;是独立模…

天选之子Linux是如何发展起来的?为何对全球IT行业的影响如此之大?

天选之子Linux是如何发展起来的&#xff1f;为何对全球IT行业的影响如此之大&#xff1f; 前言一、UNIX发展史二、Linux发展历史三、开源四、官网五、 企业应用现状六、发行版本 前言 上面这副图是博主历时半小时完成的&#xff0c;给出了Linxu的一些发展背景。球球给位看官老…

Linux—进程间通信之System V共享内存

目录 简介System V共享内存特点及用法 共享内存的创建共享内存的关联与去关联共享内存的删除共享内存通信代码实现总结 简介 System V共享内存是一种在Unix-like系统中广泛使用的共享内存机制。它是基于System V IPC&#xff08;Inter-Process Communication&#xff0c;进程间…

2023.09.30使用golang1.18编译Hel10-Web/Databasetools的windows版

#Go 1.21新增的 log/slog 完美解决了以上问题&#xff0c;并且带来了很多其他很实用的特性。 本次编译不使用log/slog 包 su - echo $GOPATH ;echo $GOROOT; cd /tmp; busybox wget --no-check-certificate https://go.dev/dl/go1.18.linux-amd64.tar.gz;\ which tar&&am…

【腾讯云 TDSQL-C Serverless 产品体验】国产数据库遥遥领先

一、为什么选TDSQL-C 1、性能达到每分钟8.14亿笔交易 国产数据库傲立世界之巅&#xff01;腾讯云数据库TDSQL-C性能刷新世界记录。 由TPC发起的TPC-C是针对在线事务处理(OLTP)的基准测试模型&#xff0c;是全球数据库厂商公认的性能评价标准&#xff0c;被誉为数据库领域的“…

解决WIFI网络登录困难的方法

当你遇到手机WIFI网络在连接成功后&#xff0c;总是提示网络受限或者当前网络无法连接互联网&#xff0c;但过一段时间后它又自动恢复正常的的问题&#xff0c;可以尝试用以下方法来解决。 第一步&#xff1a;打开WLAN连接设置界面&#xff0c;选择“更多设置” 第二步&#x…

关于TUM数据集

2、验证回环检测算法&#xff0c;需要有人工标记回环的数据集。然而人工标记回环是很不方便的&#xff0c;我们会考虑根据标准轨迹计算回环。即&#xff0c;如果轨迹中有两个帧的位姿非常相近&#xff0c;就认为它们是回环。请根据TUM数据集给出的标准轨迹&#xff0c;计算出一…