Using the @Configuration annotation

@Configuration 是一个类级别注解,用于表示一个对象是 Bean 定义的源。@Configuration 类通过带 @Bean 注释的方法声明 Bean。对 @Configuration 类上的 @Bean 方法的调用也可用于定义 Bean 间依赖项。请参阅 Basic Concepts: @Bean and @Configuration 以获取一般介绍。

Injecting Inter-bean Dependencies

当 bean 相互依赖时,表达该依赖关系与让一个 bean 方法调用另一个 bean 方法一样简单,如下例所示:

  • Java

  • Kotlin

@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 的引用。

这种声明 bean 间依赖关系的方法仅在你`@Bean` 方法在 @Configuration 类中声明时才有效。你不能使用普通 @Component 类来声明 bean 间依赖关系。

Lookup Method Injection

如前所述,lookup method injection 是一种高级功能,您应该很少使用它。当受单例作用域限制的 Bean 依赖受原型作用域限制的 Bean 时,它很有用。将 Java 用于此类配置提供了一种实现此模式的自然方式。以下示例展示如何使用查找方法注入:

  • Java

  • Kotlin

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() 方法被修改,使其以查找一个新的(原型)命令对象的方式进行。以下示例介绍如何执行此操作:

  • Java

  • Kotlin

@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()
		}
	}
}

Further Information About How Java-based Configuration Works Internally

请考虑以下示例,其中显示了注解方法 @Bean 被调用了两次:

  • Java

  • Kotlin

@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,然后才调用父方法并创建一个新的实例。

行为可能根据你的 bean 的作用域的不同而不同。我们在这里讨论的是单例。

没有必要将 CGLIB 添加到类路径,因为 CGLIB 类被重新打包在 org.springframework.cglib 包中,并直接包含在 spring-core JAR 中。

由于 CGLIB 在启动时动态添加功能,因此有一些限制。特别是,配置类不可为最终类。但是,在配置类中允许使用任何构造函数,包括使用 @Autowired 或一个用于默认注入的非默认构造函数声明。 如果你希望避免任何 CGLIB 施加的限制,请考虑在非 @Configuration 类(例如,普通 @Component 类上)上声明你的 @Bean 方法,或者通过使用 @Configuration(proxyBeanMethods = false) 对你的配置类进行注解。这样,@Bean 方法之间的交叉方法调用就不会被拦截,因此你必须专门依赖于构造函数或方法级别的依赖注入。