Skip to content

Commit 682e2d6

Browse files
committed
Introduce BeanRegistrarDsl
This commit introduces a new BeanRegistrarDsl that supersedes BeanDefinitionDsl which is now deprecated. See BeanRegistrarDslConfigurationTests for a concrete example. See spring-projectsgh-18353
1 parent 496be9c commit 682e2d6

File tree

4 files changed

+484
-2
lines changed

4 files changed

+484
-2
lines changed
Lines changed: 383 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,383 @@
1+
/*
2+
* Copyright 2002-2025 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.beans.factory
18+
19+
import org.springframework.beans.factory.BeanRegistry.SupplierContext
20+
import org.springframework.core.ParameterizedTypeReference
21+
import org.springframework.core.ResolvableType
22+
import org.springframework.core.env.Environment
23+
24+
/**
25+
* Contract for registering programmatically beans.
26+
*
27+
* Typically imported with an `@Import` annotation on `@Configuration` classes.
28+
* ```
29+
* @Configuration
30+
* @Import(MyBeanRegistrar::class)
31+
* class MyConfiguration {
32+
* }
33+
* ```
34+
*
35+
* In Kotlin, a bean registrar is typically created with a `BeanRegistrarDsl` to register
36+
* beans programmatically in a concise and flexible way.
37+
* ```
38+
* class MyBeanRegistrar : BeanRegistrarDsl({
39+
* registerBean<Foo>()
40+
* registerBean<Bar>(
41+
* name = "bar",
42+
* prototype = true,
43+
* lazyInit = true,
44+
* description = "Custom description") {
45+
* Bar(bean<Foo>())
46+
* }
47+
* profile("baz") {
48+
* registerBean { Baz("Hello World!") }
49+
* }
50+
* })
51+
* ```
52+
*
53+
* @author Sebastien Deleuze
54+
* @since 7.0
55+
*/
56+
@BeanRegistrarDslMarker
57+
open class BeanRegistrarDsl(private val init: BeanRegistrarDsl.() -> Unit): BeanRegistrar {
58+
59+
@PublishedApi
60+
internal lateinit var registry: BeanRegistry
61+
62+
/**
63+
* The environment that can be used to get the active profile or some properties.
64+
*/
65+
lateinit var env: Environment
66+
67+
/**
68+
* Register a bean from the given bean class, which will be instantiated
69+
* using the related [resolvable constructor]
70+
* [org.springframework.beans.BeanUtils.getResolvableConstructor] if any.
71+
* @param T the bean type
72+
* @param name the name of the bean
73+
* @param autowirable set whether this bean is a candidate for getting
74+
* autowired into some other bean
75+
* @param backgroundInit set whether this bean allows for instantiation
76+
* on a background thread
77+
* @param description a human-readable description of this bean
78+
* @param fallback set whether this bean is a fallback autowire candidate
79+
* @param infrastructure set whether this bean has an infrastructure role,
80+
* meaning it has no relevance to the end-user
81+
* @param lazyInit set whether this bean is lazily initialized
82+
* @param order the sort order of this bean
83+
* @param primary set whether this bean is a primary autowire candidate
84+
* @param prototype set whether this bean has a prototype scope
85+
*/
86+
inline fun <reified T : Any> registerBean(name: String,
87+
autowirable: Boolean = true,
88+
backgroundInit: Boolean = false,
89+
description: String? = null,
90+
fallback: Boolean = false,
91+
infrastructure: Boolean = false,
92+
lazyInit: Boolean = false,
93+
order: Int? = null,
94+
primary: Boolean = false,
95+
prototype: Boolean = false) {
96+
97+
val customizer: (BeanRegistry.Spec<T>) -> Unit = {
98+
if (!autowirable) {
99+
it.notAutowirable()
100+
}
101+
if (backgroundInit) {
102+
it.backgroundInit()
103+
}
104+
if (description != null) {
105+
it.description(description)
106+
}
107+
if (fallback) {
108+
it.fallback()
109+
}
110+
if (infrastructure) {
111+
it.infrastructure()
112+
}
113+
if (lazyInit) {
114+
it.lazyInit()
115+
}
116+
if (order != null) {
117+
it.order(order)
118+
}
119+
if (primary) {
120+
it.primary()
121+
}
122+
if (prototype) {
123+
it.prototype()
124+
}
125+
}
126+
registry.registerBean(name, T::class.java, customizer)
127+
}
128+
129+
/**
130+
* Register a bean from the given bean class, which will be instantiated
131+
* using the related [resolvable constructor]
132+
* [org.springframework.beans.BeanUtils.getResolvableConstructor]
133+
* if any.
134+
* @param T the bean type
135+
* @param autowirable set whether this bean is a candidate for getting
136+
* autowired into some other bean
137+
* @param backgroundInit set whether this bean allows for instantiation
138+
* on a background thread
139+
* @param description a human-readable description of this bean
140+
* @param fallback set whether this bean is a fallback autowire candidate
141+
* @param infrastructure set whether this bean has an infrastructure role,
142+
* meaning it has no relevance to the end-user
143+
* @param lazyInit set whether this bean is lazily initialized
144+
* @param order the sort order of this bean
145+
* @param primary set whether this bean is a primary autowire candidate
146+
* @param prototype set whether this bean has a prototype scope
147+
* @return the generated bean name
148+
*/
149+
inline fun <reified T : Any> registerBean(autowirable: Boolean = true,
150+
backgroundInit: Boolean = false,
151+
description: String? = null,
152+
fallback: Boolean = false,
153+
infrastructure: Boolean = false,
154+
lazyInit: Boolean = false,
155+
order: Int? = null,
156+
primary: Boolean = false,
157+
prototype: Boolean = false): String {
158+
159+
val customizer: (BeanRegistry.Spec<T>) -> Unit = {
160+
if (!autowirable) {
161+
it.notAutowirable()
162+
}
163+
if (backgroundInit) {
164+
it.backgroundInit()
165+
}
166+
if (description != null) {
167+
it.description(description)
168+
}
169+
if (fallback) {
170+
it.fallback()
171+
}
172+
if (infrastructure) {
173+
it.infrastructure()
174+
}
175+
if (lazyInit) {
176+
it.lazyInit()
177+
}
178+
if (order != null) {
179+
it.order(order)
180+
}
181+
if (primary) {
182+
it.primary()
183+
}
184+
if (prototype) {
185+
it.prototype()
186+
}
187+
}
188+
return registry.registerBean(T::class.java, customizer)
189+
}
190+
191+
/**
192+
* Register a bean from the given bean class, which will be instantiated
193+
* using the provided [supplier].
194+
* @param T the bean type
195+
* @param name the name of the bean
196+
* @param autowirable set whether this bean is a candidate for getting
197+
* autowired into some other bean
198+
* @param backgroundInit set whether this bean allows for instantiation
199+
* on a background thread
200+
* @param description a human-readable description of this bean
201+
* @param fallback set whether this bean is a fallback autowire candidate
202+
* @param infrastructure set whether this bean has an infrastructure role,
203+
* meaning it has no relevance to the end-user
204+
* @param lazyInit set whether this bean is lazily initialized
205+
* @param order the sort order of this bean
206+
* @param primary set whether this bean is a primary autowire candidate
207+
* @param prototype set whether this bean has a prototype scope
208+
* @param supplier the supplier to construct a bean instance
209+
*/
210+
inline fun <reified T : Any> registerBean(name: String,
211+
autowirable: Boolean = true,
212+
backgroundInit: Boolean = false,
213+
description: String? = null,
214+
fallback: Boolean = false,
215+
infrastructure: Boolean = false,
216+
lazyInit: Boolean = false,
217+
order: Int? = null,
218+
primary: Boolean = false,
219+
prototype: Boolean = false,
220+
crossinline supplier: (SupplierContextDsl<T>.() -> T)) {
221+
222+
val customizer: (BeanRegistry.Spec<T>) -> Unit = {
223+
if (!autowirable) {
224+
it.notAutowirable()
225+
}
226+
if (backgroundInit) {
227+
it.backgroundInit()
228+
}
229+
if (description != null) {
230+
it.description(description)
231+
}
232+
if (fallback) {
233+
it.fallback()
234+
}
235+
if (infrastructure) {
236+
it.infrastructure()
237+
}
238+
if (lazyInit) {
239+
it.lazyInit()
240+
}
241+
if (order != null) {
242+
it.order(order)
243+
}
244+
if (primary) {
245+
it.primary()
246+
}
247+
if (prototype) {
248+
it.prototype()
249+
}
250+
it.supplier {
251+
SupplierContextDsl<T>(it).supplier()
252+
}
253+
}
254+
registry.registerBean(name, T::class.java, customizer)
255+
}
256+
257+
inline fun <reified T : Any> registerBean(autowirable: Boolean = true,
258+
backgroundInit: Boolean = false,
259+
description: String? = null,
260+
fallback: Boolean = false,
261+
infrastructure: Boolean = false,
262+
lazyInit: Boolean = false,
263+
order: Int? = null,
264+
primary: Boolean = false,
265+
prototype: Boolean = false,
266+
crossinline supplier: (SupplierContextDsl<T>.() -> T)): String {
267+
/**
268+
* Register a bean from the given bean class, which will be instantiated
269+
* using the provided [supplier].
270+
* @param T the bean type
271+
* @param autowirable set whether this bean is a candidate for getting
272+
* autowired into some other bean
273+
* @param backgroundInit set whether this bean allows for instantiation
274+
* on a background thread
275+
* @param description a human-readable description of this bean
276+
* @param fallback set whether this bean is a fallback autowire candidate
277+
* @param infrastructure set whether this bean has an infrastructure role,
278+
* meaning it has no relevance to the end-user
279+
* @param lazyInit set whether this bean is lazily initialized
280+
* @param order the sort order of this bean
281+
* @param primary set whether this bean is a primary autowire candidate
282+
* @param prototype set whether this bean has a prototype scope
283+
* @param supplier the supplier to construct a bean instance
284+
*/
285+
286+
val customizer: (BeanRegistry.Spec<T>) -> Unit = {
287+
if (!autowirable) {
288+
it.notAutowirable()
289+
}
290+
if (backgroundInit) {
291+
it.backgroundInit()
292+
}
293+
if (description != null) {
294+
it.description(description)
295+
}
296+
if (infrastructure) {
297+
it.infrastructure()
298+
}
299+
if (fallback) {
300+
it.fallback()
301+
}
302+
if (lazyInit) {
303+
it.lazyInit()
304+
}
305+
if (order != null) {
306+
it.order(order)
307+
}
308+
if (primary) {
309+
it.primary()
310+
}
311+
if (prototype) {
312+
it.prototype()
313+
}
314+
it.supplier {
315+
SupplierContextDsl<T>(it).supplier()
316+
}
317+
}
318+
return registry.registerBean(T::class.java, customizer)
319+
}
320+
321+
/**
322+
* Apply the nested block if the given profile expression matches the
323+
* active profiles.
324+
*
325+
* A profile expression may contain a simple profile name (for example
326+
* `"production"`) or a compound expression. A compound expression allows
327+
* for more complicated profile logic to be expressed, for example
328+
* `"production & cloud"`.
329+
*
330+
* The following operators are supported in profile expressions:
331+
* - `!` - A logical *NOT* of the profile name or compound expression
332+
* - `&` - A logical *AND* of the profile names or compound expressions
333+
* - `|` - A logical *OR* of the profile names or compound expressions
334+
*
335+
* Please note that the `&` and `|` operators may not be mixed
336+
* without using parentheses. For example, `"a & b | c"` is not a valid
337+
* expression: it must be expressed as `"(a & b) | c"` or `"a & (b | c)"`.
338+
* @param expression the profile expressions to evaluate
339+
*/
340+
fun profile(expression: String, init: BeanRegistrarDsl.() -> Unit) {
341+
if (env.matchesProfiles(expression)) {
342+
init()
343+
}
344+
}
345+
346+
/**
347+
* Context available from the bean instance supplier designed to give access
348+
* to bean dependencies.
349+
*/
350+
@BeanRegistrarDslMarker
351+
open class SupplierContextDsl<T>(@PublishedApi internal val context: SupplierContext) {
352+
353+
/**
354+
* Return the bean instance that uniquely matches the given object type,
355+
* and potentially the name if provided, if any.
356+
* @param T the bean type
357+
* @param name the name of the bean
358+
*/
359+
inline fun <reified T : Any> bean(name: String? = null) : T = when (name) {
360+
null -> beanProvider<T>().getObject()
361+
else -> context.bean(name, T::class.java)
362+
}
363+
364+
/**
365+
* Return a provider for the specified bean, allowing for lazy on-demand
366+
* retrieval of instances, including availability and uniqueness options.
367+
* @param T type the bean must match; can be an interface or superclass
368+
* @return a corresponding provider handle
369+
*/
370+
inline fun <reified T : Any> beanProvider() : ObjectProvider<T> =
371+
context.beanProvider(ResolvableType.forType((object : ParameterizedTypeReference<T>() {}).type))
372+
}
373+
374+
override fun register(registry: BeanRegistry, env: Environment) {
375+
this.registry = registry
376+
this.env = env
377+
init()
378+
}
379+
380+
}
381+
382+
@DslMarker
383+
internal annotation class BeanRegistrarDslMarker

0 commit comments

Comments
 (0)