Using the @Configuration annotation

@Configuration 是一个类级别注解,用于表示一个对象是 Bean 定义的源。@Configuration 类通过带 @Bean 注释的方法声明 Bean。对 @Configuration 类上的 @Bean 方法的调用也可用于定义 Bean 间依赖项。请参阅 Basic Concepts: @Bean and @Configuration 以获取一般介绍。

@Configuration is a class-level annotation indicating that an object is a source of bean definitions. @Configuration classes declare beans through @Bean-annotated methods. Calls to @Bean methods on @Configuration classes can also be used to define inter-bean dependencies. See Basic Concepts: @Bean and @Configuration for a general introduction.

Injecting Inter-bean Dependencies

当 bean 相互依赖时,表达该依赖关系与让一个 bean 方法调用另一个 bean 方法一样简单,如下例所示:

When beans have dependencies on one another, expressing that dependency is as simple as having one bean method call another, as the following example shows:

  • Java

  • Kotlin

@Configuration
public class AppConfig {

	@Bean
	public BeanOne beanOne() {
		return new BeanOne(beanTwo());
	}

	@Bean
	public BeanTwo beanTwo() {
		return new BeanTwo();
	}
}
@Configuration
class AppConfig {

	@Bean
	fun beanOne() = BeanOne(beanTwo())

	@Bean
	fun beanTwo() = BeanTwo()
}

在前面的示例中,beanOne 通过构造函数注入接收对 beanTwo 的引用。

In the preceding example, beanOne receives a reference to beanTwo through constructor injection.

这种声明 bean 间依赖关系的方法仅在你`@Bean` 方法在 @Configuration 类中声明时才有效。你不能使用普通 @Component 类来声明 bean 间依赖关系。

This method of declaring inter-bean dependencies works only when the @Bean method is declared within a @Configuration class. You cannot declare inter-bean dependencies by using plain @Component classes.

Lookup Method Injection

如前所述,lookup method injection 是一种高级功能,您应该很少使用它。当受单例作用域限制的 Bean 依赖受原型作用域限制的 Bean 时,它很有用。将 Java 用于此类配置提供了一种实现此模式的自然方式。以下示例展示如何使用查找方法注入:

As noted earlier, lookup method injection is an advanced feature that you should use rarely. It is useful in cases where a singleton-scoped bean has a dependency on a prototype-scoped bean. Using Java for this type of configuration provides a natural means for implementing this pattern. The following example shows how to use lookup method injection:

  • Java

  • Kotlin

public abstract class CommandManager {
	public Object process(Object commandState) {
		// grab a new instance of the appropriate Command interface
		Command command = createCommand();
		// set the state on the (hopefully brand new) Command instance
		command.setState(commandState);
		return command.execute();
	}

	// okay... but where is the implementation of this method?
	protected abstract Command createCommand();
}
abstract class CommandManager {
	fun process(commandState: Any): Any {
		// grab a new instance of the appropriate Command interface
		val command = createCommand()
		// set the state on the (hopefully brand new) Command instance
		command.setState(commandState)
		return command.execute()
	}

	// okay... but where is the implementation of this method?
	protected abstract fun createCommand(): Command
}

通过使用 Java 配置,你可以创建一个 CommandManager 的子类,其中抽象的 createCommand() 方法被修改,使其以查找一个新的(原型)命令对象的方式进行。以下示例介绍如何执行此操作:

By using Java configuration, you can create a subclass of CommandManager where the abstract createCommand() method is overridden in such a way that it looks up a new (prototype) command object. The following example shows how to do so:

  • Java

  • Kotlin

@Bean
@Scope("prototype")
public AsyncCommand asyncCommand() {
	AsyncCommand command = new AsyncCommand();
	// inject dependencies here as required
	return command;
}

@Bean
public CommandManager commandManager() {
	// return new anonymous implementation of CommandManager with createCommand()
	// overridden to return a new prototype Command object
	return new CommandManager() {
		protected Command createCommand() {
			return asyncCommand();
		}
	}
}
@Bean
@Scope("prototype")
fun asyncCommand(): AsyncCommand {
	val command = AsyncCommand()
	// inject dependencies here as required
	return command
}

@Bean
fun commandManager(): CommandManager {
	// return new anonymous implementation of CommandManager with createCommand()
	// overridden to return a new prototype Command object
	return object : CommandManager() {
		override fun createCommand(): Command {
			return asyncCommand()
		}
	}
}

Further Information About How Java-based Configuration Works Internally

请考虑以下示例,其中显示了注解方法 @Bean 被调用了两次:

Consider the following example, which shows a @Bean annotated method being called twice:

  • Java

  • Kotlin

@Configuration
public class AppConfig {

	@Bean
	public ClientService clientService1() {
		ClientServiceImpl clientService = new ClientServiceImpl();
		clientService.setClientDao(clientDao());
		return clientService;
	}

	@Bean
	public ClientService clientService2() {
		ClientServiceImpl clientService = new ClientServiceImpl();
		clientService.setClientDao(clientDao());
		return clientService;
	}

	@Bean
	public ClientDao clientDao() {
		return new ClientDaoImpl();
	}
}
@Configuration
class AppConfig {

	@Bean
	fun clientService1(): ClientService {
		return ClientServiceImpl().apply {
			clientDao = clientDao()
		}
	}

	@Bean
	fun clientService2(): ClientService {
		return ClientServiceImpl().apply {
			clientDao = clientDao()
		}
	}

	@Bean
	fun clientDao(): ClientDao {
		return ClientDaoImpl()
	}
}

clientDao()clientService1() 中被调用了一次,而在 clientService2() 中被调用了一次。由于此方法创建了一个新的 ClientDaoImpl 实例并返回它,所以你通常会希望有两个实例(每个服务一个)。这肯定会产生问题:在 Spring 中,实例化的 bean 默认具有 singleton 作用域。这时就需要一些手段了:在启动时,所有 @Configuration 类均会被子类化为 CGLIB。在子类中,子方法首先在容器中检查任何缓存的(作用域的)bean,然后才调用父方法并创建一个新的实例。

clientDao() has been called once in clientService1() and once in clientService2(). Since this method creates a new instance of ClientDaoImpl and returns it, you would normally expect to have two instances (one for each service). That definitely would be problematic: In Spring, instantiated beans have a singleton scope by default. This is where the magic comes in: All @Configuration classes are subclassed at startup-time with CGLIB. In the subclass, the child method checks the container first for any cached (scoped) beans before it calls the parent method and creates a new instance.

行为可能根据你的 bean 的作用域的不同而不同。我们在这里讨论的是单例。

The behavior could be different according to the scope of your bean. We are talking about singletons here.

没有必要将 CGLIB 添加到类路径,因为 CGLIB 类被重新打包在 org.springframework.cglib 包中,并直接包含在 spring-core JAR 中。

It is not necessary to add CGLIB to your classpath because CGLIB classes are repackaged under the org.springframework.cglib package and included directly within the spring-core JAR.

由于 CGLIB 在启动时动态添加功能,因此有一些限制。特别是,配置类不可为最终类。但是,在配置类中允许使用任何构造函数,包括使用 @Autowired 或一个用于默认注入的非默认构造函数声明。

There are a few restrictions due to the fact that CGLIB dynamically adds features at startup-time. In particular, configuration classes must not be final. However, any constructors are allowed on configuration classes, including the use of @Autowired or a single non-default constructor declaration for default injection.

如果你希望避免任何 CGLIB 施加的限制,请考虑在非 @Configuration 类(例如,普通 @Component 类上)上声明你的 @Bean 方法,或者通过使用 @Configuration(proxyBeanMethods = false) 对你的配置类进行注解。这样,@Bean 方法之间的交叉方法调用就不会被拦截,因此你必须专门依赖于构造函数或方法级别的依赖注入。

If you prefer to avoid any CGLIB-imposed limitations, consider declaring your @Bean methods on non-@Configuration classes (for example, on plain @Component classes instead) or by annotating your configuration class with @Configuration(proxyBeanMethods = false). Cross-method calls between @Bean methods are then not intercepted, so you have to exclusively rely on dependency injection at the constructor or method level there.