对于最新的稳定版本,请使用 Spring Framework 7.0.6!spring-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 的作用域不同而有所差异。我们这里讨论的是单例模式。

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

由于CGLIB在启动时动态添加特性,存在一些限制。具体而言,配置类不能是final类。然而,配置类允许使用任何构造函数,包括使用@Autowired或单个非默认构造函数声明用于默认注入。spring-doc.cadn.net.cn

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