编程式 Bean 注册 (Programmatic Bean Registration)
从 Spring Framework 7 开始,通过 BeanRegistrar 接口提供了对编程式 Bean 注册的一流支持。你可以实现该接口,以一种灵活且高效的方式编程式地注册 Bean。
这些 Bean 注册器(Bean Registrar)实现通常通过在 @Configuration 类上使用 @Import 注解来导入。
@Configuration
@Import(MyBeanRegistrar.class)
class MyConfiguration {
}@Configuration
@Import(MyBeanRegistrar::class)
class MyConfiguration {
}提示
你可以利用类型级的条件注解(@Conditional 以及其它变体),有条件地导入相关的 Bean 注册器。
Bean 注册器实现使用 BeanRegistry 和 Environment API,以简洁灵活的方式编程式地注册 Bean。例如,它允许通过 if 表达式、for 循环等方式进行自定义注册。
class MyBeanRegistrar implements BeanRegistrar {
@Override
public void register(BeanRegistry registry, Environment env) {
registry.registerBean("foo", Foo.class);
registry.registerBean("bar", Bar.class, spec -> spec
.prototype()
.lazyInit()
.description("Custom description")
.supplier(context -> new Bar(context.bean(Foo.class))));
if (env.matchesProfiles("baz")) {
registry.registerBean(Baz.class, spec -> spec
.supplier(context -> new Baz("Hello World!")));
}
registry.registerBean(MyRepository.class);
registry.registerBean(RouterFunction.class, spec ->
spec.supplier(context -> router(context.bean(MyRepository.class))));
}
RouterFunction<ServerResponse> router(MyRepository myRepository) {
return RouterFunctions.route()
// ...
.build();
}
}class MyBeanRegistrar : BeanRegistrarDsl({
registerBean<Foo>()
registerBean(
name = "bar",
prototype = true,
lazyInit = true,
description = "Custom description") {
Bar(bean<Foo>()) // 也可以写作 Bar(bean())
}
profile("baz") {
registerBean { Baz("Hello World!") }
}
registerBean<MyRepository>()
registerBean {
myRouter(bean<MyRepository>()) // 也可以写作 myRouter(bean())
}
})
fun myRouter(myRepository: MyRepository) = router {
// ...
}注意
Bean 注册器支持运行前优化 (AOT),无论是在 JVM 上还是在使用 GraalVM 原生镜像时,即使使用了实例供应器(instance suppliers)也是如此。
补充教学 —— BeanRegistrar:Spring 7 开启的配置新篇章
在 Spring 7 之前,如果我们想动态注册 Bean,通常需要触及底层的 ImportBeanDefinitionRegistrar。虽然功能强大,但 API 相对原始且繁琐。BeanRegistrar 的出现标志着动态配置进入了“现代化”阶段。
1. 为什么我们需要 BeanRegistrar?
- 不仅仅是静态定义:
@Bean注解适合静态的、明确的 Bean 定义。但如果你需要根据数据库里的某张配置表、或者某个文件夹下的配置文件来动态生成一堆 Bean,BeanRegistrar的for循环注册就是天生利器。 - 更强的函数式支持:通过
spec.supplier(...),你可以完全控制 Bean 的实例化逻辑,这比传统的反射实例化更高效且类型安全。 - 精简的 DSL:特别是对于 Kotlin 开发者,
BeanRegistrarDsl让配置代码看起来就像官方原生语言的一部分。
2. BeanRegistrar 与传统方式的对比
- VS
@Bean:@Bean简单直观,但由于依赖代理(Full 模式),在大量 Bean 注册时有微小的性能开销。BeanRegistrar直接与BeanRegistry交互,更加底层且高效。 - VS
ImportBeanDefinitionRegistrar:BeanRegistrar是前者的后继者和简化版。它不再强制你处理深奥的BeanDefinition属性,而是提供了一个流式(Fluent)API 来设置作用域、延迟初始化等。
3. 它是如何支持 AOT 的? 这是 Spring 7 的一大卖点。以前的动态注册往往依赖大量反射和动态字节码生成,这使得它们很难被 GraalVM 静态分析。BeanRegistrar 的设计从底层就考虑了 AOT:
- 可预测性:它提供的 API 结构化程度高,Spring 的 AOT 引擎可以轻松转化这些编程式逻辑。
- Supplier 的优势:Lambda 形式的
supplier在编译期就能被转化为确定性的实例化代码,极大地提升了原生镜像的启动速度和兼容性。
4. 最佳实践
- 配合
@Conditional:不要在register方法里写太复杂的逻辑。利用@Import类上的条件注解来控制整个注册器的开关,保持职责单一。 - 优先使用参数注入获取其它 Bean:在
supplier里,推荐使用context.bean(Type.class)来按类型获取依赖,这能保证在复杂的依赖图中正确排序。
总结:如果你正在编写一个 Spring 插件、或者需要处理复杂的动态业务 Bean 场景,请毫不犹豫地拥抱 BeanRegistrar。它是 Spring 迈向“云原生”和“函数式配置”的关键一步。