如何通过@Autowired+@Qualifier注入运行时动态创建的Spring Bean?
解决方案:动态注册Bean并支持
@Autowired+@Qualifier注入 问题分析
你当前的代码存在两个核心问题:
ApplicationContextAware实现中重复注册同一Bean名称的实例,导致最终仅保留最后一次循环的Bean- 对
BeanDefinitionRegistryPostProcessor的使用存在误解,实际上它完全可以读取TestProperties中的Map属性
以下提供两种可行方案,推荐使用方案2(符合Spring规范的Bean注册方式)。
方案1:修复现有ApplicationContextAware实现
registerSingleton注册的单例Bean本身是可以被@Autowired识别的,问题出在你的循环逻辑错误。修复后即可正常使用@Autowired+@Qualifier注入。
修复后的代码
@Configuration public class DynamicBeanConfiguration implements ApplicationContextAware { private final TestProperties testProperties; public DynamicBeanConfiguration(TestProperties testProperties) { this.testProperties = testProperties; } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { ConfigurableApplicationContext configurableCtx = (ConfigurableApplicationContext) applicationContext; ConfigurableListableBeanFactory beanFactory = configurableCtx.getBeanFactory(); for (Map.Entry<String, Properties> entry : testProperties.getProperties().entrySet()) { String beanName = entry.getKey(); Properties serviceProps = entry.getValue(); // 每个服务仅创建一个Bean实例 TestDynamicBean dynamicBean = beanFactory.createBean( TestDynamicBean.class, AutowireCapableBeanFactory.AUTOWIRE_BY_NAME, false ); dynamicBean.setServiceName(beanName); // 处理属性(这里示例取第一个属性,可根据需求调整为批量处理) if (!serviceProps.isEmpty()) { Map.Entry<Object, Object> firstProp = serviceProps.entrySet().iterator().next(); dynamicBean.setValue(firstProp.getValue().toString()); } beanFactory.autowireBean(dynamicBean); // 仅注册一次该Bean名称的实例 beanFactory.registerSingleton(beanName, dynamicBean); } } }
注意事项
- 确保注入时机:不要在
@PostConstruct或更早阶段注入动态Bean,需等待上下文刷新完成 TestProperties必须正确绑定配置,需添加@ConfigurationProperties(prefix = "config")注解
方案2:使用BeanDefinitionRegistryPostProcessor注册Bean(推荐)
这是Spring官方推荐的动态注册Bean方式,生成完整的BeanDefinition,完美支持@Autowired+@Qualifier注入,且能正常读取TestProperties的Map属性。
实现动态Bean注册处理器
@Component public class DynamicBeanDefinitionProcessor implements BeanDefinitionRegistryPostProcessor { private final TestProperties testProperties; // 构造注入已绑定配置的TestProperties实例 public DynamicBeanDefinitionProcessor(TestProperties testProperties) { this.testProperties = testProperties; } @Override public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException { for (Map.Entry<String, Properties> entry : testProperties.getProperties().entrySet()) { String beanName = entry.getKey(); Properties serviceProps = entry.getValue(); // 创建BeanDefinition并配置属性 GenericBeanDefinition beanDefinition = new GenericBeanDefinition(); beanDefinition.setBeanClass(TestDynamicBean.class); beanDefinition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_NAME); MutablePropertyValues propertyValues = new MutablePropertyValues(); propertyValues.add("serviceName", beanName); if (!serviceProps.isEmpty()) { Map.Entry<Object, Object> firstProp = serviceProps.entrySet().iterator().next(); propertyValues.add("value", firstProp.getValue().toString()); } beanDefinition.setPropertyValues(propertyValues); // 注册BeanDefinition到上下文 registry.registerBeanDefinition(beanName, beanDefinition); } } @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { // 无需额外操作,如需修改已注册BeanDefinition可在此处理 } }
调整自动配置类
@Configuration @EnableConfigurationProperties(TestProperties.class) public class TestAutoConfiguration { @Bean public DynamicBeanDefinitionProcessor dynamicBeanDefinitionProcessor(TestProperties properties) { return new DynamicBeanDefinitionProcessor(properties); } }
配套TestProperties定义
确保配置绑定类正确实现:
@ConfigurationProperties(prefix = "config") public class TestProperties { private Map<String, Properties> properties; // Getter & Setter public Map<String, Properties> getProperties() { return properties; } public void setProperties(Map<String, Properties> properties) { this.properties = properties; } }
内容的提问来源于stack exchange,提问作者Roman Danileyko
相关产品推荐
相关产品推荐

