Bean Definition DSL

Spring Framework 支持使用 lambda 以函数式方式注册 bean,作为 XML 或 Java 配置(@Configuration@Bean)的替代方案。简单来说,它允许您使用充当 FactoryBean 的 lambda 来注册 bean。这种机制非常高效,因为它不需要任何反射或 CGLIB 代理。

例如,在 Java 中,您可以这样写

class Foo {}

class Bar {
	private final Foo foo;
	public Bar(Foo foo) {
		this.foo = foo;
	}
}

GenericApplicationContext context = new GenericApplicationContext();
context.registerBean(Foo.class);
context.registerBean(Bar.class, () -> new Bar(context.getBean(Foo.class)));

在 Kotlin 中,借助实化类型参数和 GenericApplicationContext Kotlin 扩展,您可以这样写

class Foo

class Bar(private val foo: Foo)

val context = GenericApplicationContext().apply {
	registerBean<Foo>()
	registerBean { Bar(it.getBean()) }
}

当类 Bar 只有一个构造函数时,您甚至只需指定 bean 类,构造函数参数将按类型自动装配

val context = GenericApplicationContext().apply {
	registerBean<Foo>()
	registerBean<Bar>()
}

为了提供更具声明性的方法和更清晰的语法,Spring Framework 提供了一个 Kotlin bean 定义 DSL。它通过一个清晰的声明式 API 声明一个 ApplicationContextInitializer,让您可以处理 profile 和 Environment,以自定义如何注册 bean。

在以下示例中请注意:

  • 类型推断通常允许避免为诸如 ref("bazBean") 的 bean 引用指定类型

  • 可以使用 Kotlin 顶层函数,通过可调用引用(例如本例中的 bean(::myRouter))来声明 bean

  • 指定 bean<Bar>()bean(::myRouter) 时,参数将按类型自动装配

  • 只有当 foobar profile 处于活动状态时,FooBar bean 才会注册

class Foo
class Bar(private val foo: Foo)
class Baz(var message: String = "")
class FooBar(private val baz: Baz)

val myBeans = beans {
	bean<Foo>()
	bean<Bar>()
	bean("bazBean") {
		Baz().apply {
			message = "Hello world"
		}
	}
	profile("foobar") {
		bean { FooBar(ref("bazBean")) }
	}
	bean(::myRouter)
}

fun myRouter(foo: Foo, bar: Bar, baz: Baz) = router {
	// ...
}
此 DSL 是编程式的,这意味着它允许通过 if 表达式、for 循环或任何其他 Kotlin 结构来实现自定义的 bean 注册逻辑。

然后,您可以使用此 beans() 函数在应用上下文中注册 bean,如下例所示

val context = GenericApplicationContext().apply {
	myBeans.initialize(this)
	refresh()
}
Spring Boot 基于 JavaConfig,目前尚不支持函数式 bean 定义,但您可以通过 Spring Boot 的 ApplicationContextInitializer 支持实验性地使用函数式 bean 定义。有关更多详细信息和最新信息,请参阅此 Stack Overflow 回答。另请参阅在 Spring Fu 孵化器中开发的实验性 Kofu DSL。