diff --git a/framework-docs/modules/ROOT/nav.adoc b/framework-docs/modules/ROOT/nav.adoc index caa288f0861..c2710277cd9 100644 --- a/framework-docs/modules/ROOT/nav.adoc +++ b/framework-docs/modules/ROOT/nav.adoc @@ -32,6 +32,7 @@ **** xref:core/beans/java/bean-annotation.adoc[] **** xref:core/beans/java/configuration-annotation.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/context-load-time-weaver.adoc[] *** xref:core/beans/context-introduction.adoc[] diff --git a/framework-docs/modules/ROOT/pages/core/beans/java/programmatic-bean-registration.adoc b/framework-docs/modules/ROOT/pages/core/beans/java/programmatic-bean-registration.adoc new file mode 100644 index 00000000000..98a5991acb8 --- /dev/null +++ b/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() + registerBean( + name = "bar", + prototype = true, + lazyInit = true, + description = "Custom description") { + Bar(bean()) + } + 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. diff --git a/framework-docs/modules/ROOT/pages/languages/kotlin/bean-definition-dsl.adoc b/framework-docs/modules/ROOT/pages/languages/kotlin/bean-definition-dsl.adoc index c2c2b9f246d..6b1752efbc2 100644 --- a/framework-docs/modules/ROOT/pages/languages/kotlin/bean-definition-dsl.adoc +++ b/framework-docs/modules/ROOT/pages/languages/kotlin/bean-definition-dsl.adoc @@ -1,113 +1,7 @@ [[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() - 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() - registerBean() - } ----- - -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()` 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() - bean() - 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]. +See xref:core/beans/java/programmatic-bean-registration.adoc[Programmatic Bean Registration].