Bean Definition DSL

Spring Framework 支持使用 Lambda 表达式(作为 XML 或 Java 配置(@Configuration@Bean)的替代方式)以函数方式注册 Bean。简而言之,它允许你使用作为 FactoryBean 的 Lambda 表达式来注册 Bean。此机制非常有效,因为它不需要任何反射或 CGLIB 代理。

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.

例如,在 Java 中,你可以编写以下内容:

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)));

而在 Kotlin 中,使用实际类型参数和 GenericApplicationContext Kotlin 扩展,你可以编写以下内容:

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()) }
}

当类 Bar 具有单个构造函数时,你甚至可以仅指定 Bean 类,构造函数参数将根据类型自动装配:

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>()
}

为了允许更具声明性的方法和更简洁的语法,Spring 框架提供了https://docs.spring.io/spring-framework/docs/current/kdoc-api/spring-context/org.springframework.context.support/-bean-definition-dsl/index.html[Kotlin Bean 定义 DSL] 它通过一个简洁的声明性 API 声明 ApplicationContextInitializer,使您可以处理配置文件和 `Environment`以自定义注册 Bean 的方式。

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>() or bean(::myRouter), parameters are autowired by type

  • The FooBar bean will be registered only if the foobar 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 {
	// ...
}

此 DSL 是以编程方式进行的,这意味着它允许通过 if 表达式、for 循环或任何其他 Kotlin 结构来自定义 bean 的注册逻辑。

This DSL is programmatic, meaning it allows custom registration logic of beans through an if expression, a for loop, or any other Kotlin constructs.

然后,你可以使用此 beans() 函数在应用上下文中注册 Bean,如下面的示例所示:

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 基于 JavaConfig 和 尚未提供对函数 bean 定义的特定支持,但你可以通过 Spring Boot 的 ApplicationContextInitializer 支持试用函数 bean 定义。有关更多详细信息和最新信息,请参见 此 Stack Overflow 答案。另请参阅 Spring Fu 孵化器 中开发的实验性 Kofu DSL。

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.