115 lines
3.6 KiB
Plaintext
115 lines
3.6 KiB
Plaintext
[[kotlin-bean-definition-dsl]]
|
|
= Bean Definition DSL
|
|
|
|
Spring Framework supports registering beans in a functional way by using lambdas
|
|
as an alternative to XML or Java configuration (`@Configuration` and `@Bean`). In a nutshell,
|
|
it lets you register beans with a lambda that acts as a `FactoryBean`.
|
|
This mechanism is very efficient, as it does not require any reflection or CGLIB proxies.
|
|
|
|
In Java, you can, for example, write the following:
|
|
|
|
[source,java,indent=0]
|
|
----
|
|
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)));
|
|
----
|
|
|
|
In Kotlin, with reified type parameters and `GenericApplicationContext` Kotlin extensions,
|
|
you can instead write the following:
|
|
|
|
[source,kotlin,indent=0]
|
|
----
|
|
class Foo
|
|
|
|
class Bar(private val foo: Foo)
|
|
|
|
val context = GenericApplicationContext().apply {
|
|
registerBean<Foo>()
|
|
registerBean { Bar(it.getBean()) }
|
|
}
|
|
----
|
|
|
|
When the class `Bar` has a single constructor, you can even just specify the bean class,
|
|
the constructor parameters will be autowired by type:
|
|
|
|
[source,kotlin,indent=0]
|
|
----
|
|
val context = GenericApplicationContext().apply {
|
|
registerBean<Foo>()
|
|
registerBean<Bar>()
|
|
}
|
|
----
|
|
|
|
In order to allow a more declarative approach and cleaner syntax, Spring Framework provides
|
|
a {spring-framework-api-kdoc}/spring-context/org.springframework.context.support/-bean-definition-dsl/index.html[Kotlin bean definition DSL]
|
|
It declares an `ApplicationContextInitializer` through a clean declarative API,
|
|
which lets you deal with profiles and `Environment` for customizing
|
|
how beans are registered.
|
|
|
|
In the following example notice that:
|
|
|
|
* Type inference usually allows to avoid specifying the type for bean references like `ref("bazBean")`
|
|
* It is possible to use Kotlin top level functions to declare beans using callable references like `bean(::myRouter)` in this example
|
|
* When specifying `bean<Bar>()` or `bean(::myRouter)`, parameters are autowired by type
|
|
* The `FooBar` bean will be registered only if the `foobar` profile is active
|
|
|
|
[source,kotlin,indent=0]
|
|
----
|
|
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 {
|
|
// ...
|
|
}
|
|
----
|
|
|
|
NOTE: This DSL is programmatic, meaning it allows custom registration logic of beans
|
|
through an `if` expression, a `for` loop, or any other Kotlin constructs.
|
|
|
|
You can then use this `beans()` function to register beans on the application context,
|
|
as the following example shows:
|
|
|
|
[source,kotlin,indent=0]
|
|
----
|
|
val context = GenericApplicationContext().apply {
|
|
myBeans.initialize(this)
|
|
refresh()
|
|
}
|
|
----
|
|
|
|
NOTE: Spring Boot is based on JavaConfig and
|
|
{spring-boot-issues}/8115[does not yet provide specific support for functional bean definition],
|
|
but you can experimentally use functional bean definitions through Spring Boot's `ApplicationContextInitializer` support.
|
|
See {stackoverflow-questions}/45935931/how-to-use-functional-bean-definition-kotlin-dsl-with-spring-boot-and-spring-w/46033685#46033685[this Stack Overflow answer]
|
|
for more details and up-to-date information. See also the experimental Kofu DSL developed in {spring-github-org}-experimental/spring-fu[Spring Fu incubator].
|
|
|
|
|
|
|
|
|