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 definition DSL。它通过一个干净的声明式API声明了一个ApplicationContextInitializer,让您处理配置文件和Environment以定制如何注册bean。

在下面的示例中,请注意:

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

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

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

  • 只有在foobar配置文件处于活动状态时,才会注册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。