取消注释CosmosDB配置方法后Spring Boot应用启动失败求助
问题排查:取消LocalCosmosDBConfig方法注释后Bean创建失败导致应用无法启动
问题背景
使用Java、Akka框架与Azure Cosmos DB开发的应用,取消LocalCosmosDBConfig.java中的cosmosAsyncClient或cosmosConfig方法注释后,出现Bean创建警告,最终抛出错误导致应用无法启动。
相关代码
CosmosDBConfig.java
@Slf4j @Configuration @ConditionalOnProperty("condition") public abstract class CosmosDBConfig extends AbstractCosmosConfiguration { protected final CosmosProperties cosmosProperties; protected CosmosDBConfig(CosmosProperties cosmosProperties) { this.cosmosProperties = cosmosProperties; } @Override public CosmosConfig cosmosConfig() { return CosmosConfig.builder() .enableQueryMetrics(cosmosProperties.isQueryMetricsEnabled()) .responseDiagnosticsProcessor( responseDiagnostics -> log.info("{} Response Diagnostics {}", getClassName(), responseDiagnostics )) .build(); } protected CosmosClientBuilder cosmosClientBuilder() { CosmosClientBuilder builder = createCosmosClientBuilder(); setCosmosCredentials(builder); return builder; } @Override public CosmosAsyncClient cosmosAsyncClient(CosmosClientBuilder cosmosClientBuilder) { return CosmosFactory.createCosmosAsyncClient(cosmosClientBuilder); } protected void setCosmosCredentials(CosmosClientBuilder builder) { if (StringUtils.hasText(getKey())) { log.info("Connecting to Cosmos DB using Azure Key Credentials"); builder.credential(new AzureKeyCredential(getKey())); } else { log.info("Connecting to Cosmos DB using Azure Token Credentials"); builder.credential(new DefaultAzureCredentialBuilder().build()); } } protected String getClassName() { return this.getClass().getSimpleName().replaceAll("\\$.*", ""); } // 子类需实现创建带专属连接属性的cosmosClientBuilder abstract CosmosClientBuilder createCosmosClientBuilder(); // 子类需实现获取专属连接密钥的逻辑 abstract String getKey(); }
LocalCosmosDBConfig.java
@Slf4j @Configuration @ConditionalOnProperty("condition") @EnableReactiveCosmosRepositories(basePackages = "com.example.repository", reactiveCosmosTemplateRef = "localDatabaseTemplate") public class LocalCosmosDBConfig extends CosmosDBConfig { private static final String LOCAL_COSMOS_CLIENT_BUILDER = "localCosmosClientBuilder"; private static final String LOCAL_COSMOS_ASYNC_CLIENT = "localCosmosAsyncClient"; private static final String LOCAL_COSMOS_CONFIG = "localCosmosConfig"; @Value("${azure.cosmos.regions}") private List<String> regions; @Value("${azure.cosmos.endpoint-discovery-enabled}") private boolean endpointDiscoveryEnabled; public LocalCosmosDBConfig(CosmosProperties cosmosProperties) { super(cosmosProperties); } @Override protected CosmosClientBuilder createCosmosClientBuilder() { log.info("Building {} Cosmos DB Connection for: {}", getClassName(), cosmosProperties.getLocalUri() ); return new CosmosClientBuilder().endpoint(cosmosProperties.getLocalUri()) .gatewayMode(new GatewayConnectionConfig()) .preferredRegions(regions) .endpointDiscoveryEnabled(endpointDiscoveryEnabled); } @Override protected String getKey() { return cosmosProperties.getLocalKey(); } @Override @Primary @Bean(LOCAL_COSMOS_CLIENT_BUILDER) public CosmosClientBuilder cosmosClientBuilder() { return super.cosmosClientBuilder(); } // @Override // @Primary // @Bean(LOCAL_COSMOS_ASYNC_CLIENT) // public CosmosAsyncClient cosmosAsyncClient( // @Qualifier(LOCAL_COSMOS_CLIENT_BUILDER) CosmosClientBuilder cosmosClientBuilder) { // log.info("Building {} Cosmos Async Client for: {}", getClassName(), cosmosProperties.getLocalUri()); // return super.cosmosAsyncClient(cosmosClientBuilder); // } // @Override // @Primary // @Bean(LOCAL_COSMOS_CONFIG) // public CosmosConfig cosmosConfig() { // return super.cosmosConfig(); // } @Bean public ReactiveCosmosTemplate localDatabaseTemplate( // @Qualifier(LOCAL_COSMOS_ASYNC_CLIENT) CosmosAsyncClient cosmosAsyncClient, // @Qualifier(LOCAL_COSMOS_CONFIG) CosmosConfig cosmosConfig, CosmosAsyncClient cosmosAsyncClient, CosmosConfig cosmosConfig, MappingCosmosConverter mappingCosmosConverter) { return new ReactiveCosmosTemplate(cosmosAsyncClient, cosmosProperties.getLocalDatabase(), cosmosConfig, mappingCosmosConverter ); } @Override protected String getDatabaseName() { return cosmosProperties.getLocalDatabase(); } @Bean public CosmosAsyncContainer buildCloudBufferAsyncContainer( // @Qualifier(LOCAL_COSMOS_ASYNC_CLIENT) CosmosAsyncClient cosmosAsyncClient CosmosAsyncClient cosmosAsyncClient ) { CosmosAsyncDatabase database = cosmosAsyncClient.getDatabase(this.getDatabaseName()); return database.getContainer(VehicleCloudEventsContainerName); } }
报错日志
[WARN] - [org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext] - Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'cosmosAsyncClient' defined in class path resource [com/example/dispatcher/configuration/LocalCosmosDBConfig.class]: No matching factory method found on class [com.example.dispatcher.configuration.LocalCosmosDBConfig]: factory bean 'localCosmosDBConfig'; factory method 'cosmosAsyncClient()'. Check that a method with the specified name exists and that it is non-static. [ERROR] - [akka.actor.SupervisorStrategy] - Error creating bean with name 'defaultValidator': Singleton bean creation not allowed while singletons of this factory are in destruction (Do not request a bean from a BeanFactory in a destroy method implementation!) akka.actor.ActorInitializationException: akka://dispatcher/user/outboundStreamActor-1: exception during creation, root cause message: [Error creating bean with name 'defaultValidator': Singleton bean creation not allowed while singletons of this factory are in destruction (Do not request a bean from a BeanFactory in a destroy method implementation!)]
原因分析
- 方法签名不匹配:Spring查找
cosmosAsyncClient工厂方法时,默认优先寻找无参方法,但子类重写的方法需要传入CosmosClientBuilder参数,且未明确指定参数来源,导致Spring无法匹配到正确的工厂方法。 - Bean冲突与优先级问题:
AbstractCosmosConfiguration会自动注册默认的cosmosAsyncClient和cosmosConfigBean,子类注册自定义Bean时,若未通过@Primary明确优先级,或未阻断默认Bean的注册,会引发Bean定义冲突,导致上下文初始化失败。 - 依赖注入不明确:
ReactiveCosmosTemplate和CosmosAsyncContainer的Bean定义中,未明确引用自定义的LOCAL_COSMOS_ASYNC_CLIENT和LOCAL_COSMOS_CONFIG,导致Spring尝试注入默认Bean,进一步加剧冲突。 - 上下文销毁连锁反应:上下文初始化失败后进入销毁阶段,此时Akka Actor尝试初始化并请求Bean,触发"Singleton bean creation not allowed while singletons are in destruction"错误。
解决办法
1. 修正自定义Bean的方法签名与注入
取消LocalCosmosDBConfig中cosmosAsyncClient和cosmosConfig方法的注释,保留参数上的@Qualifier,确保Spring能找到正确的依赖:
@Override @Primary @Bean(LOCAL_COSMOS_ASYNC_CLIENT) public CosmosAsyncClient cosmosAsyncClient( @Qualifier(LOCAL_COSMOS_CLIENT_BUILDER) CosmosClientBuilder cosmosClientBuilder) { log.info("Building {} Cosmos Async Client for: {}", getClassName(), cosmosProperties.getLocalUri()); return super.cosmosAsyncClient(cosmosClientBuilder); } @Override @Primary @Bean(LOCAL_COSMOS_CONFIG) public CosmosConfig cosmosConfig() { return super.cosmosConfig(); }
2. 明确依赖注入的Bean来源
修改localDatabaseTemplate和buildCloudBufferAsyncContainer方法,添加@Qualifier引用自定义Bean:
@Bean public ReactiveCosmosTemplate localDatabaseTemplate( @Qualifier(LOCAL_COSMOS_ASYNC_CLIENT) CosmosAsyncClient cosmosAsyncClient, @Qualifier(LOCAL_COSMOS_CONFIG) CosmosConfig cosmosConfig, MappingCosmosConverter mappingCosmosConverter) { return new ReactiveCosmosTemplate(cosmosAsyncClient, cosmosProperties.getLocalDatabase(), cosmosConfig, mappingCosmosConverter ); } @Bean public CosmosAsyncContainer buildCloudBufferAsyncContainer( @Qualifier(LOCAL_COSMOS_ASYNC_CLIENT) CosmosAsyncClient cosmosAsyncClient ) { CosmosAsyncDatabase database = cosmosAsyncClient.getDatabase(this.getDatabaseName()); return database.getContainer(VehicleCloudEventsContainerName); }
3. 禁用父类默认Bean的自动注册
在LocalCosmosDBConfig的@Configuration注解上添加@Exclude或调整@ConditionalOnProperty条件,避免AbstractCosmosConfiguration自动注册默认Bean;或者确保父类的@ConditionalOnProperty条件与子类一致,避免重复注册。
4. 验证依赖关系
检查所有Bean的依赖链,确保无循环依赖:
cosmosAsyncClient依赖localCosmosClientBuilderlocalCosmosClientBuilder依赖配置属性ReactiveCosmosTemplate依赖cosmosAsyncClient、cosmosConfig和mappingCosmosConverter
以上依赖均为单向,无循环,确保Spring能按顺序初始化Bean。
内容的提问来源于stack exchange,提问作者Venky
相关产品推荐
相关产品推荐

