Browse Source

Document programmatic bean registration

This commit adds the reference documentation for the new programmatic
bean registration capabilities for both Java and Kotlin.

Closes gh-18353
pull/34552/head
Sébastien Deleuze 10 months ago
parent
commit
bedc235bab
  1. 1
      framework-docs/modules/ROOT/nav.adoc
  2. 88
      framework-docs/modules/ROOT/pages/core/beans/java/programmatic-bean-registration.adoc
  3. 108
      framework-docs/modules/ROOT/pages/languages/kotlin/bean-definition-dsl.adoc

1
framework-docs/modules/ROOT/nav.adoc

@ -32,6 +32,7 @@
**** xref:core/beans/java/bean-annotation.adoc[] **** xref:core/beans/java/bean-annotation.adoc[]
**** xref:core/beans/java/configuration-annotation.adoc[] **** xref:core/beans/java/configuration-annotation.adoc[]
**** xref:core/beans/java/composing-configuration-classes.adoc[] **** xref:core/beans/java/composing-configuration-classes.adoc[]
**** xref:core/beans/java/programmatic-bean-registration.adoc[]
*** xref:core/beans/environment.adoc[] *** xref:core/beans/environment.adoc[]
*** xref:core/beans/context-load-time-weaver.adoc[] *** xref:core/beans/context-load-time-weaver.adoc[]
*** xref:core/beans/context-introduction.adoc[] *** xref:core/beans/context-introduction.adoc[]

88
framework-docs/modules/ROOT/pages/core/beans/java/programmatic-bean-registration.adoc

@ -0,0 +1,88 @@
[[beans-java-programmatic-registration]]
= Programmatic Bean Registration
As of Spring Framework 7, a first-class support for programmatic bean registration is
provided via the {spring-framework-api}/beans/factory/BeanRegistrar.html[`BeanRegistrar`]
interface that can be implemented to register beans programmatically in a concise and
flexible way. For example, it allows custom registration through an `if` expression, a
`for` loop, etc.
Those bean registrar implementations are typically imported with an `@Import` annotation
on `@Configuration` classes.
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
----
@Configuration
@Import(MyBeanRegistrar.class)
class MyConfiguration {
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
----
@Configuration
@Import(MyBeanRegistrar::class)
class MyConfiguration {
}
----
======
NOTE: You can leverage type-level conditional annotations ({spring-framework-api}/context/annotation/Conditional.html[`@Conditional`],
but also other variants) to conditionally import the related bean registrars.
The bean registrar implementation uses {spring-framework-api}/beans/factory/BeanRegistry.html[`BeanRegistry`] and
{spring-framework-api}/core/env/Environment.html[`Environment`] APIs to register beans programmatically in a concise
and flexible way.
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
----
class MyBeanRegistrar implements BeanRegistrar {
@Override
public void register(BeanRegistry registry, Environment env) {
registry.registerBean("foo", Foo.class);
registry.registerBean("bar", Bar.class, spec -> spec
.prototype()
.lazyInit()
.description("Custom description")
.supplier(context -> new Bar(context.bean(Foo.class))));
if (env.matchesProfiles("baz")) {
registry.registerBean(Baz.class, spec -> spec
.supplier(context -> new Baz("Hello World!")));
}
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
----
class MyBeanRegistrar : BeanRegistrarDsl({
registerBean<Foo>()
registerBean<Bar>(
name = "bar",
prototype = true,
lazyInit = true,
description = "Custom description") {
Bar(bean<Foo>())
}
profile("baz") {
registerBean { Baz("Hello World!") }
}
})
----
======
NOTE: Bean registrars are supported with xref:core/aot.adoc[Ahead of Time Optimizations],
either on the JVM or with GraalVM native images, including when instance suppliers are used.

108
framework-docs/modules/ROOT/pages/languages/kotlin/bean-definition-dsl.adoc

@ -1,113 +1,7 @@
[[kotlin-bean-definition-dsl]] [[kotlin-bean-definition-dsl]]
= Bean Definition DSL = Bean Definition DSL
Spring Framework supports registering beans in a functional way by using lambdas See xref:core/beans/java/programmatic-bean-registration.adoc[Programmatic Bean Registration].
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].

Loading…
Cancel
Save