Bean Definition DSL

Spring Framework 支援以函數式方式註冊 bean,透過使用 lambda 作為 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 的註冊方式。

在以下範例中,請注意

  • 類型推斷通常允許避免指定 bean 參考的類型,例如 ref("bazBean")

  • 可以使用 Kotlin 頂層函數來宣告 bean,在此範例中使用可呼叫參考,例如 bean(::myRouter)

  • 當指定 bean<Bar>()bean(::myRouter) 時,參數會依類型自動裝配

  • FooBar bean 僅在 foobar profile 啟用時才會註冊

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() 函數在應用程式 Context 中註冊 bean

val context = GenericApplicationContext().apply {
	myBeans.initialize(this)
	refresh()
}
Spring Boot 基於 JavaConfig,並且尚未提供對函數式 bean 定義的特定支援,但您可以透過 Spring Boot 的 ApplicationContextInitializer 支援實驗性地使用函數式 bean 定義。如需更多詳細資訊和最新資訊,請參閱此 Stack Overflow 回答。另請參閱在 Spring Fu incubator 中開發的實驗性 Kofu DSL。