Spring Boot 运行时动态创建并注入对象的核心是编程式注册Bean,主要通过GenericApplicationContext手动注册、FactoryBean按需构造、ApplicationContextInitializer刷新前植入三种方式实现。

Spring Boot 中运行时动态创建对象并注入容器,核心在于绕过编译期静态定义,改用编程方式注册 Bean。这不是“写个 new 就完事”,而是让新对象真正被 Spring 管理——能被 @Autowired 注入、参与 AOP、响应生命周期回调。关键路径有三条:基于 GenericApplicationContext 手动注册、利用 FactoryBean 延迟/按需构造、或通过 ApplicationContextInitializer 在刷新前植入。
用 GenericApplicationContext 注册普通 Bean
这是最直接的方式,适合命令行参数、配置中心变更、插件加载等场景。本质是拿到容器的注册能力,把类、构造参数、作用域等封装成 BeanDefinition 再塞进去。
- 确保使用
ConfigurableApplicationContext(如SpringApplication.run()返回的类型),再转型为GenericApplicationContext - 用
BeanDefinitionBuilder.genericBeanDefinition(clazz)构建定义,支持添加构造参数、setter 值、初始化方法 - 调用
beanFactory.registerBeanDefinition(name, beanDefinition)完成注册;之后可直接getBean(name) - 注意:同名 Bean 不会覆盖,需先
containsBean()判断,或主动removeBeanDefinition()
用 FactoryBean 控制复杂对象的生成逻辑
当对象创建过程涉及外部资源(如数据库连接、HTTP 客户端配置)、条件组装或代理封装时,FactoryBean 是更优雅的选择。它不是返回自己,而是返回“它造出来的东西”。
- 实现
FactoryBean<T>接口,重写getObject()(真正创建逻辑)、getObjectType()(声明返回类型) - 标注
@Component或在@Configuration中用@Bean方法返回该工厂实例 - Spring 会自动识别:注入
Car类型时,实际调用的是CarFactoryBean.getObject(),而非工厂本身 - 若需原型行为,重写
isSingleton()返回false,每次getBean()都新建
在上下文刷新前注册动态配置类
如果需要的不只是单个 Bean,而是一整套带 @Bean 方法的配置类(比如根据环境生成不同数据源配置),就得用 ApplicationContextInitializer。
- 编写一个类实现
ApplicationContextInitializer<ConfigurableApplicationContext> - 在
initialize()方法中,获取BeanDefinitionRegistry,用registerBeanDefinition()注册你动态构建的配置类定义 - 必须通过
spring.factories或@SpringBootApplication(initializers = ...)显式启用,否则不会触发 - 此方式避开类路径扫描限制,适用于 ByteBuddy 生成的内存类、脚本编译类等非常规来源
注意事项与避坑点
动态注册不是万能胶,几个关键约束必须清楚:
- 注册时机很重要:太早(如
ApplicationRunner里)可操作,但太晚(如 Controller 中)会报BeanDefinitionStoreException - 依赖关系要手动处理:动态 Bean 若依赖其他 Bean,不能靠字段注入,需显式从
applicationContext.getBean()获取或传入构造参数 - 不支持
@PostConstruct和@PreDestroy自动回调,需在getObject()或注册后手动触发生命周期方法 - 避免频繁注册/注销,可能引发
ConcurrentModificationException;高并发场景建议加锁或使用线程安全容器

















