使用 @Configuration 註解
@Configuration 是一個類級別的註解,表示一個物件是 bean 定義的來源。@Configuration 類透過帶有 @Bean 註解的方法宣告 bean。對 @Configuration 類上的 @Bean 方法的呼叫也可以用於定義 bean 間的依賴關係。有關一般性介紹,請參見基本概念:@Bean 和 @Configuration。
注入 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 間依賴關係。 |
查詢方法注入
如前所述,查詢方法注入是一種高階功能,您應很少使用。它在單例作用域的 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()
}
}
}
關於 Java 配置內部工作原理的更多資訊
考慮以下示例,它展示了一個帶有 @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 類已在 |
|
由於 CGLIB 在啟動時動態新增功能,因此存在一些限制。特別是,配置類不能是 final 的。但是,配置類允許任何建構函式,包括使用 如果您希望避免 CGLIB 施加的任何限制,請考慮在非 |