深入源码分析SpringBoot中使用@ConditionalOnBean无效的问题(@ConditionalOnBean did not find any beans of type)

一、前言

最近在使用SpringBoot的@ConditionalOnBean的时候遇到一个很很奇特的问题。即在@Bean中使用@ConditionalOnBean注解,在可以确保需要依赖的Bean一定在此之前已经存在了,但是@ConditionalOnBean仍然未生效。启动的时候报错:not loaded because @ConditionalOnBean (types: ***; SearchStrategy: all) did not find any beans of type ***。接下来我们一起来分析下该问题。

二、问题简介

首先我们进一步介绍下该问题。我们在jar中通过AutoConfiguration引入了RedisClientConfiguration,在其中通过@ConditionalOnBean的方式注解了一个Bean(如下图)。即需要在有Cluster实例存在的情况下才初始化注册RedicClient的是到Spring容器中。

然后我们在业务代码工程中通过XML的方式在Spring容器中定义了一个Cluster实例,只是这个实例是通过FactoryBean实现的而已。即下图中ReloadableJimClientFactoryBean是一个FactoryBean,其实际得到的是一个Cluster实例。

当我们在启动后台会报错,无法加载RedisClient实例,因为Cluster实例并不存在。

此时问题就出现了,明明在代码工程中我们在xml中定义了Cluster实例,但是为什么启动的过程中SpringBoot在OnCondition判断的却找不不到该实例呢?接下来我们就从源码的层面分下下。

三、问题分析

1、原因分析

首先我们找到ConditionalOnBean的处理源码。其在OnBeanCondition的collectBeanNamesForType中。这里就是SpringBoot通过Cluster类型去查找对应的Bean实例的地方,如果找不到对应的Bean就不会实例化。通过debug咱们可以看到此时却是无法通过beanFactory.getBeanNamesForType获取到Cluster的Bean实例。

然后咱们通过源码跟踪到具体获取代码地址:

DefaultListableBeanFactory.getBeanNamesForType 
    -> DefaultListableBeanFactory.doGetBeanNamesForType 
        -> AbstractBeanFactory.isTypeMatch 
            -> AbstractAutowireCapableBeanFactory.getTypeForFactoryBean

然后我们跳转该源码位置,发现通过通过ReloadableJimClientFactoryBean去获取其实际类型的时候,获取到了一个?,即无法和需要的类型Cluster匹配。于是返回了ResolvableType.NONE,即表示没有找到Cluster类型的Bean。

那么问题来了,为什么从ReloadableJimClientFactoryBean的实例去获取具体类型的时候是一个?而并不是其实现的Cluster呢?带着这个问题我们查看ReloadableJimClientFactoryBean的源码。通过如下源码发下其在实现FactoryBean的时候并没有显示指定泛型的类型,到时候Spring不能获取到其实现的类型。

于是我们尝试重新实现一个ReloadableJimClientFactoryBean类,让其显示指定对应泛型的类型。代码如下:

然后我们在xml中注册该类,并启动工程。启动之后无报错,于是我们再次debug查看关键部分的参数。首先AbstractAutowireCapableBeanFactory.getTypeForFactoryBean中,其能够真实获取到该FactoryBean对应的类型,且该类型为Cluster。

然后我们在OnBeanCondition的collectBeanNamesForType中可以看到,能够通过beanFactory.getBeanNamesForType获取对应的Cluster实例了。因此能够正常实例化RedisClient了。

2、问题总结

该问题存在的原因还是因为开发的时候没有怎么按规范编写代码导致的。正常情况所有泛型如果已知类型都需要没明确表示。

可以看到如果FactoryBean的类没有指定泛型类型,会导致Spring无法获取到该FactoryBean对应的实际实例的类型。所以导致了ConditionalOnBean无法通过类型找到对应的Bean实例,从而引起ConditionalOnBean注解的Bean实例无法被实例化。

四、延伸问题

在定位该问题的过程中,我遇到了另外一个导致ConditionalOnBean无法生效的问题。我们与如下配置:在业务代码(非jar)中通过@ConditionalOnBean的方式注解了一个Bean(如下图)。即需要在有Cluster实例存在的情况下才初始化注册RedicClient的是到Spring容器中。同时我们在工程中通过xml注入如JimClientFactoryBean类(该类没有上述的FactoryBean泛型类型问题)。

然后启动代码,发现同样报错,无法正常实例化Person类。

通过从源码层面分析Configuration的加载顺序(如下图),可以看到在Configuration中,BeanMethod是在XML之前完成加载,因此加载BeanMethod的时候无法读取到xml中定义的实例。源码路径:ConfigurationClassBeanDefinitionReader.loadBeanDefinitionsForConfigurationClass

于是我们不在xml中定义JimClientFactoryBean,而改在Application类中定义(如下),此时不会有任何错误,工程能够正常启动。

这里给大家一个思考问题,如果咱们将代码写成如下方式,那么工程能够正常启动么?

五、惯例

如果你对本文有任何疑问或者高见,欢迎添加公众号lifeofcoder共同交流探讨(添加公众号可以获得楼主最新博文推送以及”Java高级架构“上10G视频和图文资料哦)。

 

  • 7
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在Spring Boot 3出现 "Could not autowire. No beans of 'JdbcTemplate' type found" 错误通常是因为你没有正确配置JdbcTemplate bean。要解决这个问题,你可以按照以下步骤进行操作: 1. 确保你在项目的依赖添加了正确的JdbcTemplate依赖。你可以在pom.xml文件添加以下依赖: ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> ``` 2. 确保在配置类使用了`@EnableJdbc`注解,这将启用Spring Boot的JDBC支持。例如: ```java import org.springframework.context.annotation.Configuration; import org.springframework.jdbc.core.JdbcTemplate; @Configuration @EnableJdbc public class AppConfig { // 注入JdbcTemplate bean @Bean public JdbcTemplate jdbcTemplate(DataSource dataSource) { return new JdbcTemplate(dataSource); } } ``` 注意:上述示例假设你已经正确配置了数据源(DataSource)。 3. 确保你在需要使用JdbcTemplate的类使用了`@Autowired`注解进行自动注入。例如: ```java import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Repository; @Repository public class MyRepository { private final JdbcTemplate jdbcTemplate; @Autowired public MyRepository(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; } // 其他方法... } ``` 完成以上步骤后,重新运行应用程序,应该不再出现 "Could not autowire. No beans of 'JdbcTemplate' type found" 错误。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值