Spring如何基于配置文件列表值动态创建多个Bean并完成注入
实现方法
不要用固定@Bean注解的方式声明动态Bean,这类编译期固定的声明方式无法适配运行时从配置读取的动态列表,直接用Spring提供的BeanDefinitionRegistryPostProcessor扩展点,在Bean定义加载阶段批量注册你需要的Bean即可,步骤如下:
- 先修正已有代码的三处基础问题:
- 给配置属性类补全
@ConfigurationProperties注解,指定配置前缀,确保Spring能正确绑定配置文件中的列表值 - 修正自动配置类的构造函数名,必须和类名
ExampleAutoConfiguration完全一致,原代码写的AutoConfiguration是笔误,会导致构造注入失败 - 构造函数注入时
@Qualifier注解需要标注在构造函数参数上,标注在私有字段上不会对构造注入生效,同时参数类型统一用接口MyInterface,避免AOP代理场景下注入失败
- 给配置属性类补全
- 核心动态注册逻辑:在自动配置类中实现
BeanDefinitionRegistryPostProcessor接口,遍历读取到的配置列表,逐个生成Bean定义注册到容器,Bean名称严格按照配置项值+MyBean的规则生成,和你代码中@Qualifier指定的名称保持一致。
完整代码示例
1. 配置属性类(补全注解后)
@Data @ConfigurationProperties(prefix = "your.custom.config") // 替换为你实际的配置前缀 public class PropertiesFromConfigurationFile { String sauce; List<String> listOfSauces; // 配置示例值["foo", "bar"] }
2. 自动配置类(含动态Bean注册逻辑)
@Configuration @EnableConfigurationProperties(PropertiesFromConfigurationFile.class) public class ExampleAutoConfiguration implements BeanDefinitionRegistryPostProcessor { private final PropertiesFromConfigurationFile properties; // 修正构造函数笔误 public ExampleAutoConfiguration(PropertiesFromConfigurationFile properties) { this.properties = properties; } // 原有单Bean声明逻辑可保留,不需要可删除 @Bean public MyInterface defaultMyBean() { return new MyBean(properties.getSauce()); } @Override public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException { List<String> sauceList = properties.getListOfSauces(); if (sauceList == null || sauceList.isEmpty()) { return; } for (String sauceVal : sauceList) { // 生成和@Qualifier匹配的Bean名称:foo -> fooMyBean、bar -> barMyBean String beanName = sauceVal + "MyBean"; BeanDefinitionBuilder beanBuilder = BeanDefinitionBuilder.genericBeanDefinition(MyBean.class); // 给MyBean构造函数传入当前遍历到的配置值 beanBuilder.addConstructorArgValue(sauceVal); // 默认注册为单例Bean,可按需调整作用域 beanBuilder.setScope(BeanDefinition.SCOPE_SINGLETON); // 注册Bean到Spring容器 registry.registerBeanDefinition(beanName, beanBuilder.getBeanDefinition()); } } @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { // 无额外逻辑留空即可 } }
3. Controller注入示例(修正注解位置和参数类型)
@Controller public class ExampleController { private final MyInterface myBean; // @Qualifier标注在构造函数参数上才会生效 public ExampleController(@Qualifier("fooMyBean") MyInterface myBean) { this.myBean = myBean; } }
@Controller public class AnotherExampleController { private final MyInterface myBean; public AnotherExampleController(@Qualifier("barMyBean") MyInterface myBean) { this.myBean = myBean; } }
注意事项
- 这种方式注册的Bean和
@Bean声明的Bean完全一致,Spring会正常处理其生命周期、依赖注入、AOP代理等逻辑,没有功能差异 - 如果你的
MyBean是通过setter方法接收配置值,把代码里的addConstructorArgValue替换为addPropertyValue("对应字段名", sauceVal)即可 - 后续新增Bean不需要改Java代码,只需要在配置文件的
listOfSauces列表中添加对应字符串,启动时会自动注册对应的Bean实例
内容的提问来源于stack exchange,提问作者Florian Kleint
相关产品推荐
相关产品推荐

