12 Java-based Container Configuration
12.4 使用@Configuration注解
@Configuration是一个类级注释,指示对象是bean定义的源。@Configuration类通过公共的@Bean注释方法声明bean。在@Configuration类上调用@Bean方法也可以用来定义bean之间的依赖关系。参见Basic Concepts: @Bean and @Configuration以获得一般介绍。
注入Inter-bean依赖性
当bean彼此之间有依赖关系时,表示这种依赖关系就像让一个bean方法调用另一个bean方法一样简单,如下面的示例所示:
@Configuration
public class AppConfig {
@Bean
public BeanOne beanOne() {
return new BeanOne(beanTwo());
}
@Bean
public BeanTwo beanTwo() {
return new BeanTwo();
}
}
在前面的示例中,beanOne通过构造函数注入接收对beanTwo的引用。
只有在@Configuration类中声明@Bean方法时,这种声明bean间依赖关系的方法才有效。您不能通过使用普通的@Component类来声明bean之间的依赖关系。
查找方法注入
如前所述,lookup method injection 是一个高级特性,您应该很少使用它。
它在singleton-scoped bean依赖于prototype-scoped bean的情况下非常有用。使用Java进行这种类型的配置为实现这种模式提供了一种自然的方法。下面的例子展示了如何使用查找方法注入:
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();
}
通过使用Java配置,您可以创建CommandManager的子类,其中以查找新的(prototype)命令对象的方式覆盖了抽象的createCommand()方法。下面的例子演示了如何做到这一点:
@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();
}
}
}
关于基于java的配置如何在内部工作的更多信息
考虑下面的示例,其中显示了两次调用@Bean注释的方法
@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();
}
}
在clientService1()和clientService2()分别调用一次clientDao()。因为这个方法创建了一个新的ClientDaoImpl实例并返回它,所以您通常会希望有两个实例(每个服务一个)。这肯定会有问题:在Spring中,实例化的bean默认有一个singleton范围。这就是神奇之处:所有@Configuration类都是在启动时用CGLIB子类化的。在子类中,子方法在调用父方法并创建新实例之前,首先检查容器是否有缓存的(scoped)bean。
根据bean的范围,行为可能会有所不同。我们这里说的是singleton。
从Spring 3.2开始,不再需要将CGLIB添加到类路径中,因为CGLIB类已经在org.springframework下重新打包了。cglib并直接包含在spring-core JAR中。
由于CGLIB在启动时动态添加特性,所以有一些限制。特别是,配置类不能是final。但是,从4.3开始,配置类上可以使用任何构造函数,包括使用@Autowired或单个非默认构造函数声明进行默认注入。
如果希望避免cglib强加的限制,可以考虑在非@Configuration 配置类上声明@Bean方法(例如,在普通的@Component类上声明)。然后不会拦截@Bean方法之间的交叉方法调用,因此必须完全依赖于构造函数或方法级别的依赖注入。