You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring如何基于配置文件列表值动态创建多个Bean并完成注入

实现方法

不要用固定@Bean注解的方式声明动态Bean,这类编译期固定的声明方式无法适配运行时从配置读取的动态列表,直接用Spring提供的BeanDefinitionRegistryPostProcessor扩展点,在Bean定义加载阶段批量注册你需要的Bean即可,步骤如下:

  • 先修正已有代码的三处基础问题:
    1. 给配置属性类补全@ConfigurationProperties注解,指定配置前缀,确保Spring能正确绑定配置文件中的列表值
    2. 修正自动配置类的构造函数名,必须和类名ExampleAutoConfiguration完全一致,原代码写的AutoConfiguration是笔误,会导致构造注入失败
    3. 构造函数注入时@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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 20:24:28