12 Java-based Container Configuration
本節的內容:如何使用註解配置 Spring 容器。包含下面的主題:
- 基本概念:@Bean 和 @Configuration
- 使用AnnotationConfigApplication初始化Spring容器
- 使用@Bean註解
- 使用@Configuration
- 編寫基於java的配置
- Bean定義概要
- PropertySource抽象
- 使用@PropertySource
- 語句中佔位符的解析
12.3使用@Bean註解
@Bean是方法級別的註解,與XML bean元素作用相同。該註解支持一些XML的bean元素中的一些屬性,比如: * init-method * destroy-method * autowiring * 等屬性。
定義Bean
要聲明一個bean,可以使用@Bean註釋來註解一個方法。使用@Bean註解的方法註冊一個Bean定義作爲該方法的返回值,註冊的Bean在ApplicationContext的類型規範內。默認情況下,bean名與方法名相同。下面的例子顯示了一個@Bean方法聲明:
@Configuration
public class AppConfig {
@Bean
public TransferServiceImpl transferService() {
return new TransferServiceImpl();
}
}
前面的配置與下面的Spring XML完全相同
<beans>
<bean id="transferService" class="com.acme.TransferServiceImpl"/>
</beans>
這兩個聲明都使一個名爲transferService的bean在ApplicationContext中可用,綁定到類型爲TransferServiceImpl的對象實例,如下面的文本圖像所示:
transferService -> com.acme.TransferServiceImpl
您還可以使用接口(或基類)返回類型聲明@Bean方法,如下面的示例所示:
@Configuration
public class AppConfig {
@Bean
public TransferService transferService() {
return new TransferServiceImpl();
}
}
但是,這將高級類型預測的可見性限制爲指定的接口類型(TransferService)。然後,容器只知道一次完整類型(TransferServiceImpl),受影響的單例bean就被實例化了。非延遲性單例bean根據它們的聲明順序被實例化,因此您可能會看到不同的類型匹配結果,這取決於另一個組件何時試圖通過未聲明的類型進行匹配(例如@Autowired TransferServiceImpl,它只在transferService bean實例化之後纔會解析)。
注意
如果始終通過聲明的服務接口引用類型,則@Bean返回類型可以安全地與設計的意圖相吻合。但是,對於實現多個接口的組件或可能由其實現類型引用的組件,聲明最特定的返回類型會更安全。(至少與引用bean的注入點所要求的一樣具體)
Bean依賴關係
@bean註解的方法可以有任意數量的參數來描述構建該bean所需的依賴關係。
例如,如果我們的TransferService需要一個AccountRepository,我們可以用一個方法參數來實現這個依賴,如下面的例子所示:
@Configuration
public class AppConfig {
@Bean
public TransferService transferService(AccountRepository accountRepository) {
return new TransferServiceImpl(accountRepository);
}
}
解析機制與基於構造的依賴注入非常相似。有關詳細信息,請參閱Constructor-based Dependency Injection。
接收生命週期回調
使用@Bean註解定義的任何類都支持常規的生命週期回調,並且可以使用JSR-250中的@PostConstruct和@PreDestroy註解。也完全支持常規的Spring生命週期回調。如果一個bean實現了InitializingBean、DisposableBean或Lifecycle,容器將調用它們各自的方法。標準的*Aware接口集(如BeanFactoryAware、BeanNameAware、MessageSourceAware、ApplicationContextAware等)也得到了完全支持。
@Bean註釋支持指定任意初始化和銷燬回調方法,很像Spring XML在bean元素上的init-method和destroy-method屬性,如下面的示例所示:
public class BeanOne {
public void init() {
// initialization logic
}
}
public class BeanTwo {
public void cleanup() {
// destruction logic
}
}
@Configuration
public class AppConfig {
@Bean(initMethod = "init")
public BeanOne beanOne() {
return new BeanOne();
}
@Bean(destroyMethod = "cleanup")
public BeanTwo beanTwo() {
return new BeanTwo();
}
}
注意
默認情況下,Java配置定義的bean擁有public 修飾的close或shtudown方法將被自動加入到銷燬回調中。如果有public
修飾的close或shtudown方法,並且您不希望在容器關閉時調用它,那麼您可以向bean定義中添加@Bean(destroyMethod="")來禁用默認(推斷)模式。
在默認情況下,您可能希望對使用JNDI獲得的資源這樣做,因爲它的生命週期是在應用程序之外管理的。特別要注意的是,一定要始終對Datasource這樣做,因爲在JavaEE應用服務器上這樣做是有問題的。 下面的示例演示如何防止Datasource的自動銷燬回調@Bean(destroyMethod="") public DataSource dataSource() throws NamingException { return (DataSource) jndiTemplate.lookup("MyDS"); }
同樣,對於@Bean方法,您通常使用可編程的JNDI查找,可以使用Spring的JndiTemplate或JndiLocatorDelegate助手,或者直接使用JNDI InitialContext,但不使用JndiObjectFactoryBean變體(這將迫使您將返回類型聲明爲FactoryBean類型,而不是實際的目標類型,這使得在其他想要引用這裏提供的資源的@Bean方法中進行交叉引用調用時更加困難)。
對於上例中的BeanOne,在構造期間直接調用init()方法同樣有效,如下例所示
@Configuration
public class AppConfig {
@Bean
public BeanOne beanOne() {
BeanOne beanOne = new BeanOne();
beanOne.init();
return beanOne;
}
// ...
}
當您直接在Java中工作時,您可以對對象做任何您喜歡的事情,而不總是需要依賴容器生命週期。
確定Bean的作用域
Spring包含@Scope註解,因此您可以指定bean的範圍。
您可以指定使用@Bean註解定義的bean應該具有特定的範圍。您可以使用Bean Scope部分中指定的任何標準作用域。
Scope | Description |
---|---|
singleton | (默認)爲每個Spring IoC容器將單個bean定義作用於單個對象實例。 |
prototype | 將單個bean定義作用於任意數量的對象實例。 |
request | 將單個bean定義的範圍限定爲單個HTTP請求的生命週期。也就是說,每個HTTP請求都有自己的bean實例,這些實例是在單個bean定義的基礎上創建的。僅在可感知web的Spring應用程序上下文中有效。 |
session | 將單個bean定義的範圍限定爲HTTP會話的生命週期。僅在可感知web的Spring應用程序上下文中有效。 |
application | 將單個bean定義作用於ServletContext的生命週期。僅在可感知web的Spring應用程序上下文中有效。 |
websocket | 將單個bean定義作用於WebSocket的生命週期。僅在可感知web的Spring應用程序上下文中有效。 |
從Spring 3.0開始,線程作用域可用,但默認情況下不註冊。有關更多信息,請參閱SimpleThreadScope的文檔。有關如何註冊此或任何其他自定義範圍的說明,請參閱Using a Custom Scope一章。
默認的範圍是單例的,但是你可以用@Scope註解來覆蓋它,如下面的例子所示:
@Configuration
public class MyConfiguration {
@Bean
@Scope("prototype")
public Encryptor encryptor() {
// ...
}
}
Spring通過作用域代理提供了處理作用域依賴項的方便方法。使用XML配置時創建這樣一個代理的最簡單方法是aop:scoped-proxy元素。用@Scope註釋在Java中配置bean可以提供與proxyMode屬性相同的支持。默認值是no proxy (ScopedProxyMode. no),但是您可以指定ScopedProxyMode。TARGET_CLASS或ScopedProxyMode.INTERFACES。
如果您使用Java將XML參考文檔中的作用域代理示例(參見作用域代理)移植到我們的@Bean,它類似於以下內容:
// an HTTP Session-scoped bean exposed as a proxy
@Bean
@SessionScope
public UserPreferences userPreferences() {
return new UserPreferences();
}
@Bean
public Service userService() {
UserService service = new SimpleUserService();
// a reference to the proxied userPreferences bean
service.setUserPreferences(userPreferences());
return service;
}
定製化Bean命名
默認情況下,配置類使用@Bean方法的名稱作爲結果bean的名稱。但是,可以使用name屬性覆蓋此功能,如下面的示例所示:
@Configuration
public class AppConfig {
@Bean(name = "myThing")
public Thing thing() {
return new Thing();
}
}
Bean別名
正如在bean的命名中所討論的,有時希望爲單個bean賦予多個名稱,稱爲bean別名。@Bean註釋的name屬性爲此接受一個字符串數組。下面的例子展示瞭如何爲一個bean設置多個別名:
@Configuration
public class AppConfig {
@Bean({"dataSource", "subsystemA-dataSource", "subsystemB-dataSource"})
public DataSource dataSource() {
// instantiate, configure and return DataSource bean...
}
}
Bean 描述
有時,提供bean的更詳細的文本描述是有幫助的。當出於監控目的而公開bean(可能通過JMX)時,這一點特別有用
要向@Bean添加描述,可以使用@Description註釋,如下面的示例所示:
@Configuration
public class AppConfig {
@Bean
@Description("Provides a basic example of a bean")
public Thing thing() {
return new Thing();
}
}