使用Prototype作用域创建Spring DataSource遇依赖问题求助
场景与配置
尝试创建Prototype作用域的DriverManagerDataSource Bean,数据库连接信息在运行时传入,相关配置代码如下:
配置类代码
@Configuration class Cfg { @Bean public Function<DataSourcePropertiesMap, DriverManagerDataSource> functionOfDriverMgrDS() { return this::driverManagerDataSource; } @Lazy @Bean @Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE) public DriverManagerDataSource driverManagerDataSource(DataSourcePropertiesMap dbPropsMap) { var ds = new DriverManagerDataSource(dbPropsMap.getDbURL(), dbPropsMap.getDbUsername(), dbPropsMap.getDbPassword()); ds.setDriverClassName(dbPropsMap.getDbDriver()); return ds; } }
参数容器类代码
@Getter @AllArgsConstructor public class DataSourcePropertiesMap { @NonNull private final String dbURL; @NonNull private final String dbUsername; @NonNull private final String dbPassword; @NonNull private final String dbDriver; }
启动异常
应用启动时抛出依赖缺失异常:
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'healthContributorRegistry' defined in class path resource [org/springframework/boot/actuate/autoconfigure/health/HealthEndpointConfiguration.class]: Unsatisfied dependency expressed through method 'healthContributorRegistry' parameter 2; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'dbHealthContributor' defined in class path resource [org/springframework/boot/actuate/autoconfigure/jdbc/DataSourceHealthContributorAutoConfiguration.class]: Unsatisfied dependency expressed through method 'dbHealthContributor' parameter 0; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'driverManagerDataSource' defined in class path resource [Cfg.class]: Unsatisfied dependency expressed through method 'driverManagerDataSource' parameter 0; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'DataSourcePropertiesMap' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
疑问
原本认为Prototype作用域的Bean会在调用时传入参数创建新实例,为何Spring仍要求存在DataSourcePropertiesMap类型的Bean?若配置该类的默认Bean,还会要求提供有效的驱动类。
原因分析与解决方案
原因
问题出在Spring Boot Actuator的自动配置:DataSourceHealthContributorAutoConfiguration会自动探测容器中所有的DataSource Bean,在应用启动时尝试初始化实例以进行健康检查。即使是Prototype作用域的Bean,Actuator触发创建时,Spring会尝试从容器中查找driverManagerDataSource方法的依赖DataSourcePropertiesMap,但该类并未注册为Spring Bean,因此抛出缺失依赖的异常。
而Prototype Bean的参数传入逻辑仅在主动调用ApplicationContext.getBean(Class, Object...)或通过Function手动创建时生效,容器自动触发的Bean创建(如Actuator自动装配)会遵循常规的依赖注入规则,从容器中查找依赖。
解决方案
方案1:排除DataSource健康检查自动配置
在启动类上添加注解,排除Actuator的数据源健康检查配置,避免其自动初始化DataSource Bean:
@SpringBootApplication @EnableAutoConfiguration(exclude = DataSourceHealthContributorAutoConfiguration.class) public class YourApplication { public static void main(String[] args) { SpringApplication.run(YourApplication.class, args); } }
方案2:不将DataSource注册为Spring Bean,通过Function完全控制创建
修改配置类,去掉driverManagerDataSource方法上的@Bean、@Scope等注解,仅保留创建DataSource的Function Bean,由业务代码手动调用创建实例:
@Configuration class Cfg { @Bean public Function<DataSourcePropertiesMap, DriverManagerDataSource> functionOfDriverMgrDS() { return this::driverManagerDataSource; } // 普通方法,不注册为Spring Bean private DriverManagerDataSource driverManagerDataSource(DataSourcePropertiesMap dbPropsMap) { var ds = new DriverManagerDataSource(dbPropsMap.getDbURL(), dbPropsMap.getDbUsername(), dbPropsMap.getDbPassword()); ds.setDriverClassName(dbPropsMap.getDbDriver()); return ds; } }
业务代码中注入Function并创建数据源:
@Service public class DataSourceService { private final Function<DataSourcePropertiesMap, DriverManagerDataSource> dsCreator; public DataSourceService(Function<DataSourcePropertiesMap, DriverManagerDataSource> dsCreator) { this.dsCreator = dsCreator; } public DriverManagerDataSource createRuntimeDataSource(String url, String username, String password, String driver) { DataSourcePropertiesMap props = new DataSourcePropertiesMap(url, username, password, driver); return dsCreator.apply(props); } }
方案3:禁用特定数据源的健康检查
如果需要保留DataSource作为Spring管理的Prototype Bean,可在配置文件中禁用Actuator的数据库健康检查:
# 禁用所有数据库健康检查 management.health.db.enabled=false
或者通过自定义健康指示器,仅检查特定数据源,避免触发目标Prototype Bean的初始化。
内容的提问来源于stack exchange,提问作者Kryptix

