当前位置:主页 > java教程 > SpringBoot中@ConditionalOnBean实现原理

SpringBoot中@ConditionalOnBean实现原理解读

发布:2023-03-31 20:00:01 59


本站精选了一篇相关的编程文章,网友桓飞跃根据主题投稿了本篇教程内容,涉及到SpringBoot、@ConditionalOnBean、@ConditionalOnBean实现原理、SpringBoot、@ConditionalOnBean原理、SpringBoot中@ConditionalOnBean实现原理相关内容,已被269网友关注,相关难点技巧可以阅读下方的电子资料。

SpringBoot中@ConditionalOnBean实现原理

SpringBoot @ConditionalOnBean实现原理

在SpringBoot1.5.X时

判断条件是OR,SpringBoot2.x变为了AND

在spring 启动过程中,会调用BeanDefinitionRegistryPostProcessor的postProcessBeanDefinitionRegistry后置处理器 调用ComponentScanAnnotationParser的parse(..)方法解析配置类,实现类路径下的类文件扫描,

遍历被扫描到的类,并通过@ComponentScan注解上指定的规则进行匹配,默认情况下,SpringBoot扫描@Component标记的类,( 但是在spring.factories中通过key org.springframework.boot.autoconfigure.EnableAutoConfiguration指定配置不会被扫描. 这些配置类会由@EnableAutoConfiguration导入的AutoConfigurationImportSelector来处理);

如果出现匹配的类还会通过ConditionEvaluator.shouldSkip(...)来判断是否忽略这个类,这时候会过滤掉小部分无效类,但是不会在这个期间应用@ConditionalOnBean去验证,

因为其验证实现类OnBeanCondition实现了接口ConfigurationCondition的getConfigurationPhase()方法,该方法返回值是一个枚举值,用于判断什么时期应用这个条件验证, shouldSkip()方法会判断这个接口返回值,OnBeanCondition实现的getConfigurationPhase()返回的是ConfigurationPhase.REGISTER_BEAN,而类扫描期间只会执行没有实现ConfigurationCondition接口,或返回值为null,或为ConfigurationPhase.PARSE_CONFIGURATION的验证器.

所以这个期间不会应用@ConditionalOnBean去验证. 这也可以理解因为这个时候有很多类没有被遍历到,现在执行OnBeanCondition验证肯定多数是false.

这也导致很多最终不会被实例化为Spring bean的类被包装成BeanDefinition(实现类ScannedGenericBeanDefinition ),并注册在DefaultListableBeanFactory 的成员beanDefinitionMap中,key为Bean的名称,同时Bean的名称会保存在List集合beanDefinitionNames中, 对于一些手动注册的Bean的名称会保存在List集合manualSingletonNames中,

然后对于parse(..)返回的BeanDefinition集合会遍历调用ConfigurationClassParser的parse(...)递归调用, 当解析到配置中有@Bean定义的Bean,并有条件注解时,会调用ConditionEvaluator.shouldSkip(...)方法,最终调用到的是 Condition实现类的matches(...)方法.

而@ConditionalOnBean注解中的验证实现类OnBeanCondition,最终判断是否有指定的bean就是通过DefaultListableBeanFactory 中的beanDefinitionMap和manualSingletonNames是否包含指定的Bean的名称来验证的.

当然对于扫描到的无效类,在ConfigurationClassPostProcessor的processConfigBeanDefinitions(...)调用parse()方法后会调用this.reader.loadBeanDefinitions(configClasses),来判断最终有效的BeanDefinition,这时候就会执行OnBeanCondition, 移除beanDefinitionMap中无效的BeanDefinition. 

鉴于类路径扫描规则和OnBeanCondition验证实现原理

就可能导致无效问题

例如如下代码:

C希望不存在A Bean时创建,B希望在C bean存在时创建,示例中A配置了Bean,这时B C都不应该创建,但执行结果是C确实没有创建,而B却被创建了.

原因就在于类扫描时,C被扫描了,导致beanDefinitionMap存在C 的BeanDefinition.

但是C在this.reader.loadBeanDefinitions(configClasses),中由于条件验证不通过没有创建被移除.(当然和类扫描顺序也有关系,如果B能够在C之后被扫描到,就会执行正常,所以Spring官网建议通过spring.factories来配置,指定执行顺序或者在能够确定执行顺序的情况下使用@ConditionalOnBean,但是一定不能通过文件名来控制顺序)

注意点

如果将示例中的类配置到spring.factories中即时不指定顺序,也能正常执行,不会创建B C,这是由于spring.factories配置文件中的类并不是全部包装成BeanDefinition然后判断是否有效的,而是排序后封装成ConfigurationClass通过遍历的方式,同样是在this.reader.loadBeanDefinitions(configClasses)方法中判断该类是否通过条件验证是否有效.

当执行到B是,C还没有被处理,所以导致B也不会被创建,(这有点属于巧合导致的正确结果,所以为了安全还是要执行顺序,让B在C之后被处理)

第二种情况, 在配置类中定义Bean,如果使用@ConditionalOnBean依赖的也是配置类中Bean,则执行结果不可控,和配置类加载顺序有关。

示例代码如下:代码中是希望Bean3被创建,但是实际上并没有!!!

解决有两种方式:

1 可以考虑使用@DependsOn来解决,参看Spring中@DependsOn注解的作用及实现原理。

2 如果一定要区分两个配置类的先后顺序,可以将这两个类交与EnableAutoConfiguration管理和触发。 也就是定义在META-INF\spring.factories中声明是配置类, 然后通过@AutoConfigureBefore、AutoConfigureAfter、AutoConfigureOrder控制先后顺序。 因为这三个注解只对自动配置类生效。

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持码农之家。


参考资料

相关文章

  • SpringBoot如何用Spring-Data-Jpa实现CRUD操作

    发布:2021-05-25

    这篇文章主要为大家详细介绍了SpringBoot使用Spring-Data-Jpa实现CRUD操作,具有一定的参考价值,感兴趣的小伙伴们可以参考一下


  • SpringBoot在Controller层接收参数的n种姿势(超详细)

    发布:2023-03-11

    这篇文章主要介绍了SpringBoot在Controller层接收参数的常用方法,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下


  • SpringBoot注册FilterRegistrationBean相关情况讲解

    发布:2023-04-16

    这篇文章主要介绍了SpringBoot注册FilterRegistrationBean相关情况,借助FilterRegistrationBean来注册filter,可以避免在web.xml种配置filter这种原始的写法


  • SpringBoot访问windows共享文件的方法

    发布:2023-04-12

    这篇文章主要介绍了SpringBoot访问windows共享文件,项目使用minio存储且不在同一台服务器上,为了优化速度决定使用windows共享功能进行文件传输,本文结合实例代码给大家介绍的非常详细,需要的朋友可以参考下


  • springboot使用alibaba的druid数据库连接池错误的问题及解决

    发布:2023-03-22

    这篇文章主要介绍了springboot使用alibaba的druid数据库连接池错误的问题及解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教


  • SpringBoot整合Redis的实现示例

    发布:2023-04-23

    本文主要介绍了SpringBoot整合Redis的实现示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧


  • SpringBoot Bean花式注解方法示例下篇

    发布:2023-04-06

    这篇文章主要介绍了SpringBoot Bean花式注解方法,很多时候我们需要根据不同的条件在容器中加载不同的Bean,或者根据不同的条件来选择是否在容器中加载某个Bean


  • springboot+webmagic实现java爬虫jdbc及mysql实例代码

    发布:2020-02-10

    今天小编就为大家分享一篇springboot+webmagic实现java爬虫jdbc及mysql的方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧


网友讨论