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。 |