使用 @Configuration
注解
@Configuration
是一个类级别注解,指示该对象是 Bean 定义的来源。@Configuration
类通过 @Bean
注解的方法声明 Bean。在 @Configuration
类上调用 @Bean
方法也可以用来定义 Bean 之间的依赖关系。关于通用介绍,请参阅基本概念:@Bean
和 @Configuration
。
注入 Bean 之间的依赖关系
当 Bean 之间存在依赖关系时,表达这种依赖就像一个 Bean 方法调用另一个 Bean 方法一样简单,示例如下
-
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
的引用。
这种声明 Bean 之间依赖关系的方法仅在 @Bean 方法声明在 @Configuration 类内部时有效。你不能通过普通的 @Component 类来声明 Bean 之间的依赖关系。 |
Lookup 方法注入
如前所述,lookup 方法注入是一个高级特性,应很少使用。当单例作用域的 Bean 依赖于原型作用域的 Bean 时,此特性会非常有用。使用 Java 进行此类配置为实现此模式提供了一种自然的方式。下面的示例展示了如何使用 lookup 方法注入
-
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()
被重写,以便它查找新的(原型)命令对象。以下示例展示了如何实现这一点
-
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()
}
}
}
关于基于 Java 的配置如何工作的更多内部信息
考虑以下示例,它展示了一个 @Bean
注解的方法被调用两次
-
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。
根据你的 Bean 的作用域不同,行为也可能不同。我们这里讨论的是单例。 |
你无需将 CGLIB 添加到你的类路径中,因为 CGLIB 类已被重新打包到 |
由于 CGLIB 在启动时动态添加功能,存在一些限制。特别是,配置类不能是 final 的。然而,配置类允许使用任何构造函数,包括使用 如果你希望避免 CGLIB 施加的任何限制,可以考虑在非 |