This version is still in development and is not considered stable yet. For the latest stable version, please use Spring Framework 6.2.1! |
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:
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:
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:
val context = GenericApplicationContext().apply {
registerBean<Foo>()
registerBean<Bar>()
}
In order to allow a more declarative approach and cleaner syntax, Spring Framework provides
a 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>()
orbean(::myRouter)
, parameters are autowired by type -
The
FooBar
bean will be registered only if thefoobar
profile is active
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 {
// ...
}
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:
val context = GenericApplicationContext().apply {
myBeans.initialize(this)
refresh()
}
Spring Boot is based on JavaConfig and
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 this Stack Overflow answer
for more details and up-to-date information. See also the experimental Kofu DSL developed in Spring Fu incubator.
|