Skip to content

Latest commit

 

History

History
119 lines (86 loc) · 4.91 KB

File metadata and controls

119 lines (86 loc) · 4.91 KB

3.12.4  使用@Configuration注解

@Configuration是一个类级别的注解,表名该类将作为bean定义的配置元数据。 @Configuration类通过public@Bean注解方法声明bean。 在@Configuration类上调用@Bean方法也可以用于定义bean间依赖关系。 参见第3.12.1节“基本概念:@Bean和@Configuration” 里的介绍。

注入内部bean依赖

@Bean彼此有依赖关系时,表示依赖关系就像调用另一个bean方法一样简单:

@Configuration
public class AppConfig {

	@Bean
	public Foo foo() {
		return new Foo(bar());
	}

	@Bean
	public Bar bar() {
		return new Bar();
	}

}

在上面的例子中,foo bean通过构造函数注入接收对bar的引用。

[Note]
这种方式仅仅适用于在@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();
		}
	}
}

更多关于Java配置内部工作的信息

下面示例中,展示了@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默认情况下有一个singletonscope。 这就是它的神奇之处:所有@Configuration类在启动时都是通过CGLIB创建一个子类。 在子类中,在调用父类的方法并创建一个新的实例之前,子类中的方法首先检查是否缓存过该bean实例。 注意,从Spring 3.2开始,不再需要将CGLIB添加到classpath中,因为CGLIB类已经在org.springframework.cglib下重新打包并直接包含在spring-core JAR中,可以直接使用。

[Note]
这种行为可以根据bean的作用域而变化,我们这里只讨论单例。
[Tip]
实际上还会有一些限制,因为CGLIB是在启动的时候动态地添加这些特性,所以配置的类不能是final的。但是,从4.3开始,允许在配置类上使用任何构造函数,包括使用@Autowired或一个非默认构造函数声明作为默认注入。如果想避免任何CGLIB带来的限制,请考虑声明非@Configuration类的@Bean方法,例如 在纯的@Component类。 这样在@Bean方法之间的交叉方法调用将不会被拦截,因此你必须在构造函数或方法级别上依赖于依赖注入。