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。