Skip to content

Commit bedc235

Browse files
committed
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
1 parent 682e2d6 commit bedc235

File tree

3 files changed

+90
-107
lines changed

3 files changed

+90
-107
lines changed

Diff for: framework-docs/modules/ROOT/nav.adoc

+1
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
**** xref:core/beans/java/bean-annotation.adoc[]
3333
**** xref:core/beans/java/configuration-annotation.adoc[]
3434
**** xref:core/beans/java/composing-configuration-classes.adoc[]
35+
**** xref:core/beans/java/programmatic-bean-registration.adoc[]
3536
*** xref:core/beans/environment.adoc[]
3637
*** xref:core/beans/context-load-time-weaver.adoc[]
3738
*** xref:core/beans/context-introduction.adoc[]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
[[beans-java-programmatic-registration]]
2+
= Programmatic Bean Registration
3+
4+
As of Spring Framework 7, a first-class support for programmatic bean registration is
5+
provided via the {spring-framework-api}/beans/factory/BeanRegistrar.html[`BeanRegistrar`]
6+
interface that can be implemented to register beans programmatically in a concise and
7+
flexible way. For example, it allows custom registration through an `if` expression, a
8+
`for` loop, etc.
9+
10+
Those bean registrar implementations are typically imported with an `@Import` annotation
11+
on `@Configuration` classes.
12+
13+
[tabs]
14+
======
15+
Java::
16+
+
17+
[source,java,indent=0,subs="verbatim,quotes"]
18+
----
19+
@Configuration
20+
@Import(MyBeanRegistrar.class)
21+
class MyConfiguration {
22+
}
23+
----
24+
25+
Kotlin::
26+
+
27+
[source,kotlin,indent=0,subs="verbatim,quotes"]
28+
----
29+
@Configuration
30+
@Import(MyBeanRegistrar::class)
31+
class MyConfiguration {
32+
}
33+
----
34+
======
35+
36+
NOTE: You can leverage type-level conditional annotations ({spring-framework-api}/context/annotation/Conditional.html[`@Conditional`],
37+
but also other variants) to conditionally import the related bean registrars.
38+
39+
The bean registrar implementation uses {spring-framework-api}/beans/factory/BeanRegistry.html[`BeanRegistry`] and
40+
{spring-framework-api}/core/env/Environment.html[`Environment`] APIs to register beans programmatically in a concise
41+
and flexible way.
42+
43+
[tabs]
44+
======
45+
Java::
46+
+
47+
[source,java,indent=0,subs="verbatim,quotes"]
48+
----
49+
class MyBeanRegistrar implements BeanRegistrar {
50+
51+
@Override
52+
public void register(BeanRegistry registry, Environment env) {
53+
registry.registerBean("foo", Foo.class);
54+
registry.registerBean("bar", Bar.class, spec -> spec
55+
.prototype()
56+
.lazyInit()
57+
.description("Custom description")
58+
.supplier(context -> new Bar(context.bean(Foo.class))));
59+
if (env.matchesProfiles("baz")) {
60+
registry.registerBean(Baz.class, spec -> spec
61+
.supplier(context -> new Baz("Hello World!")));
62+
}
63+
}
64+
}
65+
----
66+
67+
Kotlin::
68+
+
69+
[source,kotlin,indent=0,subs="verbatim,quotes"]
70+
----
71+
class MyBeanRegistrar : BeanRegistrarDsl({
72+
registerBean<Foo>()
73+
registerBean<Bar>(
74+
name = "bar",
75+
prototype = true,
76+
lazyInit = true,
77+
description = "Custom description") {
78+
Bar(bean<Foo>())
79+
}
80+
profile("baz") {
81+
registerBean { Baz("Hello World!") }
82+
}
83+
})
84+
----
85+
======
86+
87+
NOTE: Bean registrars are supported with xref:core/aot.adoc[Ahead of Time Optimizations],
88+
either on the JVM or with GraalVM native images, including when instance suppliers are used.

Diff for: framework-docs/modules/ROOT/pages/languages/kotlin/bean-definition-dsl.adoc

+1-107
Original file line numberDiff line numberDiff line change
@@ -1,113 +1,7 @@
11
[[kotlin-bean-definition-dsl]]
22
= Bean Definition DSL
33

4-
Spring Framework supports registering beans in a functional way by using lambdas
5-
as an alternative to XML or Java configuration (`@Configuration` and `@Bean`). In a nutshell,
6-
it lets you register beans with a lambda that acts as a `FactoryBean`.
7-
This mechanism is very efficient, as it does not require any reflection or CGLIB proxies.
8-
9-
In Java, you can, for example, write the following:
10-
11-
[source,java,indent=0]
12-
----
13-
class Foo {}
14-
15-
class Bar {
16-
private final Foo foo;
17-
public Bar(Foo foo) {
18-
this.foo = foo;
19-
}
20-
}
21-
22-
GenericApplicationContext context = new GenericApplicationContext();
23-
context.registerBean(Foo.class);
24-
context.registerBean(Bar.class, () -> new Bar(context.getBean(Foo.class)));
25-
----
26-
27-
In Kotlin, with reified type parameters and `GenericApplicationContext` Kotlin extensions,
28-
you can instead write the following:
29-
30-
[source,kotlin,indent=0]
31-
----
32-
class Foo
33-
34-
class Bar(private val foo: Foo)
35-
36-
val context = GenericApplicationContext().apply {
37-
registerBean<Foo>()
38-
registerBean { Bar(it.getBean()) }
39-
}
40-
----
41-
42-
When the class `Bar` has a single constructor, you can even just specify the bean class,
43-
the constructor parameters will be autowired by type:
44-
45-
[source,kotlin,indent=0]
46-
----
47-
val context = GenericApplicationContext().apply {
48-
registerBean<Foo>()
49-
registerBean<Bar>()
50-
}
51-
----
52-
53-
In order to allow a more declarative approach and cleaner syntax, Spring Framework provides
54-
a {spring-framework-api-kdoc}/spring-context/org.springframework.context.support/-bean-definition-dsl/index.html[Kotlin bean definition DSL]
55-
It declares an `ApplicationContextInitializer` through a clean declarative API,
56-
which lets you deal with profiles and `Environment` for customizing
57-
how beans are registered.
58-
59-
In the following example notice that:
60-
61-
* Type inference usually allows to avoid specifying the type for bean references like `ref("bazBean")`
62-
* It is possible to use Kotlin top level functions to declare beans using callable references like `bean(::myRouter)` in this example
63-
* When specifying `bean<Bar>()` or `bean(::myRouter)`, parameters are autowired by type
64-
* The `FooBar` bean will be registered only if the `foobar` profile is active
65-
66-
[source,kotlin,indent=0]
67-
----
68-
class Foo
69-
class Bar(private val foo: Foo)
70-
class Baz(var message: String = "")
71-
class FooBar(private val baz: Baz)
72-
73-
val myBeans = beans {
74-
bean<Foo>()
75-
bean<Bar>()
76-
bean("bazBean") {
77-
Baz().apply {
78-
message = "Hello world"
79-
}
80-
}
81-
profile("foobar") {
82-
bean { FooBar(ref("bazBean")) }
83-
}
84-
bean(::myRouter)
85-
}
86-
87-
fun myRouter(foo: Foo, bar: Bar, baz: Baz) = router {
88-
// ...
89-
}
90-
----
91-
92-
NOTE: This DSL is programmatic, meaning it allows custom registration logic of beans
93-
through an `if` expression, a `for` loop, or any other Kotlin constructs.
94-
95-
You can then use this `beans()` function to register beans on the application context,
96-
as the following example shows:
97-
98-
[source,kotlin,indent=0]
99-
----
100-
val context = GenericApplicationContext().apply {
101-
myBeans.initialize(this)
102-
refresh()
103-
}
104-
----
105-
106-
NOTE: Spring Boot is based on JavaConfig and
107-
{spring-boot-issues}/8115[does not yet provide specific support for functional bean definition],
108-
but you can experimentally use functional bean definitions through Spring Boot's `ApplicationContextInitializer` support.
109-
See {stackoverflow-questions}/45935931/how-to-use-functional-bean-definition-kotlin-dsl-with-spring-boot-and-spring-w/46033685#46033685[this Stack Overflow answer]
110-
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].
4+
See xref:core/beans/java/programmatic-bean-registration.adoc[Programmatic Bean Registration].
1115

1126

1137

0 commit comments

Comments
 (0)