@Configuration
是一个类级别的注解,表名该类将作为bean定义的配置元数据。 @Configuration
类通过public@Bean
注解方法声明bean。 在@Configuration
类上调用@Bean
方法也可以用于定义bean间依赖关系。 参见第3.12.1节“基本概念:@Bean和@Configuration” 里的介绍。
当@Bean
彼此有依赖关系时,表示依赖关系就像调用另一个bean方法一样简单:
@Configuration
public class AppConfig {
@Bean
public Foo foo() {
return new Foo(bar());
}
@Bean
public Bar bar() {
return new Bar();
}
}
在上面的例子中,foo
bean通过构造函数注入接收对bar
的引用。
这种方式仅仅适用于在@Configuration内部定义的@Bean方法。在普通的@Component类中不能声明内部依赖。 |
如前所述, lookup method injection 是一种高级 功能,你应该很少使用。。但是,在一个单例bean依赖原型作用域bean的场景中,就非常有用了。Java中,提供了很友好的api实现此模式。
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
的子类,其中抽象createCommand()
方法被覆盖,这样就可以让它查找到新的原型command对象:
@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 command() overridden
// to return a new prototype Command object
return new CommandManager() {
protected Command createCommand() {
return asyncCommand();
}
}
}
下面示例中,展示了@Bean
注解的方法被调用了2次:
@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();
}
}
clientDao()
在clientService1()
中调用一次,在clientService2()
中调用一次。 由于这个方法创建一个新的ClientDaoImpl
实例并返回它,你通常期望有两个实例(每个服务一个实例)。 这肯定会有问题:在Spring中,实例化的bean默认情况下有一个singleton
scope。 这就是它的神奇之处:所有@Configuration
类在启动时都是通过CGLIB
创建一个子类。 在子类中,在调用父类的方法并创建一个新的实例之前,子类中的方法首先检查是否缓存过该bean实例。 注意,从Spring 3.2开始,不再需要将CGLIB添加到classpath中,因为CGLIB类已经在org.springframework.cglib下重新打包并直接包含在spring-core JAR中,可以直接使用。
这种行为可以根据bean的作用域而变化,我们这里只讨论单例。 |