12 Java-based Container Configuration
12.4 使用@Configuration註解
@Configuration是一個類級註釋,指示對象是bean定義的源。@Configuration類通過公共的@Bean註釋方法聲明bean。在@Configuration類上調用@Bean方法也可以用來定義bean之間的依賴關係。參見Basic Concepts: @Bean and @Configuration以獲得一般介紹。
注入Inter-bean依賴性
當bean彼此之間有依賴關係時,表示這種依賴關係就像讓一個bean方法調用另一個bean方法一樣簡單,如下面的示例所示:
@Configuration
public class AppConfig {
@Bean
public BeanOne beanOne() {
return new BeanOne(beanTwo());
}
@Bean
public BeanTwo beanTwo() {
return new BeanTwo();
}
}
在前面的示例中,beanOne通過構造函數注入接收對beanTwo的引用。
只有在@Configuration類中聲明@Bean方法時,這種聲明bean間依賴關係的方法纔有效。您不能通過使用普通的@Component類來聲明bean之間的依賴關係。
查找方法注入
如前所述,lookup method injection 是一個高級特性,您應該很少使用它。
它在singleton-scoped bean依賴於prototype-scoped bean的情況下非常有用。使用Java進行這種類型的配置爲實現這種模式提供了一種自然的方法。下面的例子展示瞭如何使用查找方法注入:
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的子類,其中以查找新的(prototype)命令對象的方式覆蓋了抽象的createCommand()方法。下面的例子演示瞭如何做到這一點:
@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();
}
}
}
關於基於java的配置如何在內部工作的更多信息
考慮下面的示例,其中顯示了兩次調用@Bean註釋的方法
@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();
}
}
在clientService1()和clientService2()分別調用一次clientDao()。因爲這個方法創建了一個新的ClientDaoImpl實例並返回它,所以您通常會希望有兩個實例(每個服務一個)。這肯定會有問題:在Spring中,實例化的bean默認有一個singleton範圍。這就是神奇之處:所有@Configuration類都是在啓動時用CGLIB子類化的。在子類中,子方法在調用父方法並創建新實例之前,首先檢查容器是否有緩存的(scoped)bean。
根據bean的範圍,行爲可能會有所不同。我們這裏說的是singleton。
從Spring 3.2開始,不再需要將CGLIB添加到類路徑中,因爲CGLIB類已經在org.springframework下重新打包了。cglib並直接包含在spring-core JAR中。
由於CGLIB在啓動時動態添加特性,所以有一些限制。特別是,配置類不能是final。但是,從4.3開始,配置類上可以使用任何構造函數,包括使用@Autowired或單個非默認構造函數聲明進行默認注入。
如果希望避免cglib強加的限制,可以考慮在非@Configuration 配置類上聲明@Bean方法(例如,在普通的@Component類上聲明)。然後不會攔截@Bean方法之間的交叉方法調用,因此必須完全依賴於構造函數或方法級別的依賴注入。