对于最新稳定版本,请使用 Spring Framework 7.0.6spring-doc.cadn.net.cn

使用 @Configuration 注解

@Configuration 是一个类级别的注解,用于指示某个对象是 Bean 定义的来源。@Configuration 类通过带有 @Bean 注解的方法来声明 Bean。调用 @Configuration 类上的 @Bean 方法也可用于定义 Bean 之间的依赖关系。有关一般性介绍,请参阅 基本概念:@Bean@Configurationspring-doc.cadn.net.cn

注入 Bean 之间的依赖关系

当 Bean 之间存在依赖关系时,表达这种依赖关系就像让一个 Bean 方法调用另一个 Bean 方法一样简单,如下例所示:spring-doc.cadn.net.cn

@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 的引用。spring-doc.cadn.net.cn

这种声明 Bean 之间依赖关系的方法仅在 @Bean 方法被声明于 @Configuration 类中时才有效。您不能通过普通的 @Component 类来声明 Bean 之间的依赖关系。

查找方法注入

如前所述,查找方法注入是一项高级功能,应尽量少用。当单例作用域的 bean 依赖于原型作用域的 bean 时,该功能非常有用。使用 Java 进行此类配置为实现这种模式提供了一种自然的方式。以下示例展示了如何使用查找方法注入:spring-doc.cadn.net.cn

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() 被重写,以查找一个新的(原型)命令对象。以下示例展示了如何实现这一点:spring-doc.cadn.net.cn

@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 注解的方法被调用了两次:spring-doc.cadn.net.cn

@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。spring-doc.cadn.net.cn

根据您的 Bean 的作用域不同,其行为可能会有所不同。这里我们讨论的是单例(singleton)。

无需将 CGLIB 添加到您的类路径中,因为 CGLIB 类已被重新打包到 org.springframework.cglib 包下,并直接包含在 spring-core JAR 文件中。spring-doc.cadn.net.cn

由于 CGLIB 在启动时动态添加功能,因此存在一些限制。特别是,配置类不能是 final 的。不过,配置类可以包含任意构造函数,包括使用 @Autowired 注解,或者声明一个非默认的单一构造函数以用于默认注入。spring-doc.cadn.net.cn

如果你希望避免 CGLIB 带来的任何限制,可以考虑将你的 @Bean 方法声明在非 @Configuration 类上(例如,直接放在普通的 @Component 类中),或者通过使用 @Configuration(proxyBeanMethods = false) 注解来标注你的配置类。这样一来,@Bean 方法之间的跨方法调用将不会被拦截,因此你必须完全依赖构造函数或方法级别的依赖注入。spring-doc.cadn.net.cn