Spring Boot中@ConditionalOnBean搭配@Qualifier使用异常求解
Spring Boot @ConditionalOnBean条件判断提前触发导致Bean未创建的启动异常问题
问题场景
在Spring Boot项目中配置条件化Bean时遇到启动依赖注入异常,相关代码配置如下:
AssumeRole核心配置类:
@Configuration @Import(AssumeRoleSQSConfig.class) public class AssumeRoleConfig { public static final DynamicStringProperty ROLE_ARN = DynamicPropertyFactory.getInstance() .getStringProperty("role.arn", null); public static final DynamicStringProperty ROLE_SESSION = DynamicPropertyFactory.getInstance() .getStringProperty("role.session.name", null); @Bean(name = "assumeRole") public AWSCredentialsProvider getCredentials() { if (ROLE_ARN.get() == null || ROLE_SESSION.get() == null) { return null; } // 省略业务逻辑代码 } }
已在config.properties中正确配置
role.arn与role.session.name两个属性。
AssumeRole对应的SQS配置类:
@Configuration public class AssumeRoleSQSConfig { @Bean(name = "sqsAssumeBean") @ConditionalOnBean(name = "assumeRole") public AmazonSQSAsync sqsAssume( @Qualifier("assumeRole") AWSCredentialsProvider awsCredentials) { // 省略业务逻辑代码 } }
默认凭证与默认SQS配置类:
@Configuration @Import(DefaultSQSConfig.class) public class DefaultCredentialsConfig { @Bean @Primary public AWSCredentialsProvider defaultCred() { return new DefaultAWSCredentialsProviderChain(); } }
@Configuration public class DefaultSQSConfig { @Bean(destroyMethod = "destroy") @Primary public AmazonSQSAsync defaultSQS() { // 省略业务逻辑代码 } }
队列配置类,尝试注入两个SQS实例:
@Configuration @Import({DefaultCredentialsConfig.class, AssumeRoleConfig.class}) public class QueueConfig { private AmazonSQSAsync defaultSQS; private AmazonSQSAsync assumeRoleSQS; @Autowired public QueueConfig ( @Qualifier("defaultSQS") AmazonSQSAsync defaultSQS, @Qualifier("sqsAssumeBean") AmazonSQSAsync assumeRoleSQS) { this.defaultSQS = defaultSQS; this.assumeRoleSQS = assumeRoleSQS; } }
启动时抛出的异常信息:
Error creating bean with name 'queueConfig' defined in ... QueueConfig.class Unsatisfied dependency expressed through constructor parameter 1; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.amazonaws.services.sqs.AmazonSQSAsync' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Qualifier(value="sqsAssumeBean")} Description: Parameter 1 of constructor in QueueConfig required a bean of type 'com.amazonaws.services.sqs.AmazonSQSAsync' that could not be found. The following candidates were found but could not be injected: - User-defined bean method 'defaultSQS' in 'DefaultSQSConfig'
调试发现AssumeRoleConfig的getCredentials方法完全未被调用,只有移除@ConditionalOnBean注解时,才会尝试创建assumeRole Bean。预期逻辑为:当ROLE_ARN、ROLE_SESSION动态属性为空时,不创建assumeRole Bean,进而级联不创建sqsAssumeBean、QueueConfig Bean;但目前即使两个动态属性都已配置,assumeRole Bean的创建方法也不会触发。
问题根因
核心原因是**@ConditionalOnBean的判断执行时机早于Bean方法的实际调用,且判断逻辑不感知Bean方法返回null的场景**,具体分为两点:
@ConditionalOnBean是Spring Boot提供的配置类解析阶段的条件注解,它判断的是「当前已经被解析注册的Bean定义」中是否存在指定名称/类型的Bean,而不是等Bean实例化完成后再判断。当前配置类加载顺序下,解析AssumeRoleSQSConfig时,AssumeRoleConfig里的assumeRoleBean定义还没完成注册,直接被判定为不存在,所以sqsAssumeBean根本不会进入实例化流程。- 其次,
@Bean方法返回null不会被Spring识别为「不创建该Bean」,反而会在实际调用该方法创建Bean时抛出异常,靠方法内判断返回null来跳过Bean创建的写法本身就不符合Spring Bean的生命周期规则。
修复方案
- 不要在
@Bean方法内返回null控制Bean是否创建,改用标准的条件注解:给assumeRoleBean加上@ConditionalOnProperty注解,直接基于配置属性判断是否创建,不需要在方法内写判空逻辑。@Bean(name = "assumeRole") @ConditionalOnProperty(prefix = "role", name = {"arn", "session.name"}) public AWSCredentialsProvider getCredentials() { // 属性不存在时这个Bean根本不会被注册,不需要额外判空 // 省略业务逻辑代码 } - 调整配置类的加载顺序,保证被依赖的Bean所在的配置类先被解析:给
AssumeRoleSQSConfig加上@AutoConfigureAfter(AssumeRoleConfig.class)注解,确保解析sqsAssumeBean的条件时,assumeRole的Bean定义已经完成注册。 - 对于
QueueConfig中可选的assumeRoleSQS依赖,标记为可选注入,避免Bean不存在时直接启动失败:@Autowired public QueueConfig ( @Qualifier("defaultSQS") AmazonSQSAsync defaultSQS, @Qualifier("sqsAssumeBean") @Autowired(required = false) AmazonSQSAsync assumeRoleSQS) { this.defaultSQS = defaultSQS; this.assumeRoleSQS = assumeRoleSQS; }
内容的提问来源于stack exchange,提问作者contemplator
相关产品推荐
相关产品推荐

