####7.9基於註解的容器配置 ####前言:註解配置好仍是xml配置好? 兩者各有千秋.java
在字節碼文件裏注入組件而不是角括號申明.開發者能夠將註解添加在相關的類,方法,屬性聲明上歷來注入依賴組件.使用bean後處理器來糅合註解是spring ioc 容器擴展的通用方式.例如,spring2.0引用了強制要求的@Required註解.值得一提的是,@Autowired註解提供了和7.4.5部分中的"Autowiring collaborators"相同的做用功能,但具備更好的控制和更廣的應用.@PostConstruct,@PreDestroy,@Inject,@Namedspring
你能夠在xml裏明確的指出.數組
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <context:annotation-config/> </beans>
已詳細註冊的後處理器,包括AutowiredAnnotationBeanPostProcessor,CommonAnnotationBeanPostProcessor,PersistenceAnnotationBeanPostProcessor,RequiredAnnotationBeanPostProcessor;緩存
context:annotation-config/只查找上下文中定義的註解. ####7.9.1 @Required 這個註解使用bean中屬性的Setter方法;app
public class SimpleMovieLister { private MovieFinder movieFinder; @Required public void setMovieFinder(MovieFinder movieFinder) { this.movieFinder = movieFinder; } // ... }
這個註解申明其標註的屬性必須在配置時間被操做,經過一些指定值或經過自動裝配.若是屬性沒有被操做,spring啓動會有明確的失敗,以免以後的空指針異常.使用它能夠保障必須引用.工具
####7.9.2 @Autowired @Inject註解能夠替代@Autowired註解. 你可使用@Inject註解來替代@Autowired註解;ui
能夠在構造器中使用@Autowired註解this
public class MovieRecommender { private final CustomerPreferenceDao customerPreferenceDao; @Autowired public MovieRecommender(CustomerPreferenceDao customerPreferenceDao) { this.customerPreferenceDao = customerPreferenceDao; } // ... }
在spring4.3中,bean若是隻定義了一個構造器,@Autowired註解沒有使用必要.多個構造器才必須這麼作.代理
通常而言,若是沒有可選的beans,那麼註解將會失敗,默認的行爲是將該註解下的方法,字段,構造器視爲必須的依賴,然能夠以下改變它.指針
public class SimpleMovieLister { private MovieFinder movieFinder; @Autowired(required=false) public void setMovieFinder(MovieFinder movieFinder) { this.movieFinder = movieFinder; } // ... }
一個類只有一個構造器標誌爲required,但non-required的構造器能夠有不少.這種狀況下,爲了使每個候選者都會考慮,spring將會使用最貪婪的構造器(擁有最多的參數),這樣依賴都會適配.
@Autowired註解裏的Required屬性,若是是false,代表若是沒法注入,是能夠接受的,不是必須的.若是是true,則是必須的,若是沒法注入,容器沒法啓動
你能夠用@Autowired來解決一些經常使用的依賴:Beanfactory,ApplicationContext,Environment,ResourceLoader,ApplicationEventPublisher,MessageSource.這些接口和他們的擴展接口,如ConfigurableApplicationContext,ResourcePatternResolver,均可以自注入.無需安裝.
如@Autowired,@Inject,@Resource,@Value註解只能被spring的BeanPostProcessor實現處理,你本身的beanPostProcessor或BeanPostProcessor類型沒法處理他們.這些類型必須由xml或spring的帶有Bean的方法來注入.
由於自動注入可能會致使混合選項,全部有必要對其選擇過程進行控制.一種方式是使用@Primary註解.@Primary代表一個特定的bean在不少同一類型的候選中,必需要選擇該bean的實例注入.
@Configuration public class MovieConfiguration { @Bean @Primary public MovieCatalog firstMovieCatalog() { ... } @Bean public MovieCatalog secondMovieCatalog() { ... } // ... }
下文中注入的是名爲firstMovieCatalog的bean的實例; public class MovieRecommender {
@Autowired private MovieCatalog movieCatalog; // ...
}
xml配置文件以下:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <context:annotation-config/> <bean class="example.SimpleMovieCatalog" primary="true"> <!-- inject any dependencies required by this bean --> </bean> <bean class="example.SimpleMovieCatalog"> <!-- inject any dependencies required by this bean --> </bean> <bean id="movieRecommender" class="example.MovieRecommender"/> </beans>
####7.9.4 Fine-tuning annotation-based autowiring with qualifiers(基於註解的候選者的自動注入的微調) @Qualifier是@Autowired控制的另外一個工具.
public class MovieRecommender {
@Autowired @Qualifier("main") private MovieCatalog movieCatalog; // ...
}
這個@Qualifier註解能夠指定構造器參數或方法參數;
public class MovieRecommender { private MovieCatalog movieCatalog; private CustomerPreferenceDao customerPreferenceDao; @Autowired public void prepare(@Qualifier("main")MovieCatalog movieCatalog, CustomerPreferenceDao customerPreferenceDao) { this.movieCatalog = movieCatalog; this.customerPreferenceDao = customerPreferenceDao; } // ... }
根據上面的的類定義,將會匹配qualifier的值爲'mian'的bean
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <context:annotation-config/> <bean class="example.SimpleMovieCatalog"> <qualifier value="main"/> <!-- inject any dependencies required by this bean --> </bean> <bean class="example.SimpleMovieCatalog"> <qualifier value="action"/> <!-- inject any dependencies required by this bean --> </bean> <bean id="movieRecommender" class="example.MovieRecommender"/> </beans>
通常而言,bean的名字會默認是qualifier的值.所以,你可使用'main'來替代內嵌的限定元素(它會致使相同的匹配結果).然而,儘管你可使用本公約以名字來制定特定的bean,然@Autowired其實是類型驅動注入包含語義限制的.這意味着限定符的值,同bean的名字回收,一般只是窄化類型匹配的集合;他們沒有從語法上指向一個獨一無二的bean的id.好的限制符,如'main','EMEA'或者'persistent',表達了一個特定的不一樣於bean的id的獨立組件,畢竟bean的id會在匿名的bean定義裏自動產生;
Qualifiers也適用於類型集合,例如,上文所說的Set<MovieCatalog>.在這種狀況下,全部匹配的bean會依據宣佈的匹配符注入爲一個集合.它代表限制符不是惟一的,他們只是簡單的構成過濾條件.例如,你能夠定義混合的MovieCatalog的bean以相同的限制符'action'.全部的符合@Qualifier("action")條件的bean將注入到Set<MovieCatalog>集合裏.
若是你要傾向於經過名字的註解驅動注入,不推薦@Autowired,即便經過@Qualifier值來指向一個bean是技術可行的.你應該使用@Resource註解,它在語義上經過惟一的名字來定義目標的組件,而他們的申明類型並不會參與匹配.@Atuowired則是另外的語義:經過類型選擇候選者以後,纔會考慮特定的string的qualifier的值,只有匹配該值的qualifier的bean才能注入.
對於定義成list/map或者數組類型的bean是來講,@Resource是一個好的方案,經過一個惟一的名字指向特定的數組或集合.這意味着,在4.3中,集合或數組類型能夠經過spring的@Autowired進行類型匹配.
在4.3中,@Autowired也能夠考慮自注入,即引用到正在注入的bean.自注入是一個回退;常規注入仍然有優先級.也就是說,自引用不是常規候選項,所以他沒有優先級;相反,他們(自注入)的優先級是最低的.實際上,使用自引用是最後的手段.例如:經過bean的事務代理調用相同bean的其餘方法,在此場景下能夠考慮將受影響的方法分解到單獨的代理bean中.
@Autowired適用於字段,構造器,混成參數方法,經過@Qualifier註解來限制參數範圍.相反的,@Resource註解支持字段,bean的單參數的set方法.所以,它會嚴格按照qualifiers來匹配你構造器或混合參數方法的注入目標.
######使用自定義的qualifier; 定義以下:
@Target({ElementType.FIELD, ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) @Qualifier public @interface Genre { String value(); }
以下使用該註解
public class MovieRecommender { @Autowired @Genre("Action") private MovieCatalog actionCatalog; private MovieCatalog comedyCatalog; @Autowired public void setComedyCatalog(@Genre("Comedy") MovieCatalog comedyCatalog) { this.comedyCatalog = comedyCatalog; } // ... }
你能夠在xml配置文件裏以下配置:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <context:annotation-config/> <bean class="example.SimpleMovieCatalog"> <qualifier type="Genre" value="Action"/> <!-- inject any dependencies required by this bean --> </bean> <bean class="example.SimpleMovieCatalog"> <qualifier type="example.Genre" value="Comedy"/> <!-- inject any dependencies required by this bean --> </bean> <bean id="movieRecommender" class="example.MovieRecommender"/> </beans>
即在<bean>標籤裏使用<qualifier>標籤,其屬性爲type,value;
使用無具體值的自定義qualifier;
註解Offline定義以下:
@Target({ElementType.FIELD, ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) @Qualifier public @interface Offline { }
public class MovieRecommender { @Autowired @Offline private MovieCatalog offlineCatalog; // ... }
<bean class="example.SimpleMovieCatalog"> <qualifier type="Offline"/> <!-- inject any dependencies required by this bean --> </bean>
你能夠自定義一個註解接受命名參數來替代簡單的註解,
@Target({ElementType.FIELD, ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) @Qualifier public @interface MovieQualifier { String genre(); Format format(); }
Format枚舉以下:
public enum Format { VHS, DVD, BLURAY }
這些字段經過自定義匹配符註解來自動注入的包括如下屬性的值:genre,format.
public class MovieRecommender { @Autowired @MovieQualifier(format=Format.VHS, genre="Action") private MovieCatalog actionVhsCatalog; @Autowired @MovieQualifier(format=Format.VHS, genre="Comedy") private MovieCatalog comedyVhsCatalog; @Autowired @MovieQualifier(format=Format.DVD, genre="Action") private MovieCatalog actionDvdCatalog; @Autowired @MovieQualifier(format=Format.BLURAY, genre="Comedy") private MovieCatalog comedyBluRayCatalog; // ... }
最後,這個bean定義必須包含匹配符的值.這個例子也說明bean的元屬性能夠用來替代<qualifier>的下級元素.通常狀況下,<qualifier/>和它的屬性有優先級,但若是沒顯示的匹配符,那麼自動裝配機制將會使用meta標籤.下面的例子至少有兩種bean定義形式;
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <context:annotation-config/> <bean class="example.SimpleMovieCatalog"> <qualifier type="MovieQualifier"> <attribute key="format" value="VHS"/> <attribute key="genre" value="Action"/> </qualifier> <!-- inject any dependencies required by this bean --> </bean> <bean class="example.SimpleMovieCatalog"> <qualifier type="MovieQualifier"> <attribute key="format" value="VHS"/> <attribute key="genre" value="Comedy"/> </qualifier> <!-- inject any dependencies required by this bean --> </bean> <bean class="example.SimpleMovieCatalog"> <meta key="format" value="DVD"/> <meta key="genre" value="Action"/> <!-- inject any dependencies required by this bean --> </bean> <bean class="example.SimpleMovieCatalog"> <meta key="format" value="BLURAY"/> <meta key="genre" value="Comedy"/> <!-- inject any dependencies required by this bean --> </bean> </beans>
####7.9.5 使用泛型做爲自動裝配的匹配符 除了@Qualifier註解外,你還能夠用java原生類型做爲匹配符的一種形式,通常狀況下,你須要如此配置:
@Configuration public class MyConfiguration { @Bean public StringStore stringStore() { return new StringStore(); } @Bean public IntegerStore integerStore() { return new IntegerStore(); } }
上面的bean實現了泛型接口,好比Store<String>和Store<Integer>,你可使用@Autowire進行注入,泛型將會做爲匹配符:
@Autowired private Store<String> s1; // <String> qualifier, injects the stringStore bean @Autowired private Store<Integer> s2; // <Integer> qualifier, injects the integerStore bean
泛型匹配符也能夠用來注入List,Maps,Arrays
// Inject all Store beans as long as they have an <Integer> generic // Store<String> beans will not appear in this list @Autowired private List<Store<Integer>> s;
###7.9.6 CustomAutowireConfigurer(自定義自動專配配置器) 使用了CustomAutowireConfigurer(自己是一個BeanFactoryPostProcessor組件)以後,即便你的自定義qualifier沒有加上@Qualifier註解,你也能夠註冊你的自定義qualifier;
<bean id="customAutowireConfigurer" class="org.springframework.beans.factory.annotation.CustomAutowireConfigurer"> <property name="customQualifierTypes"> <set> <value>example.CustomQualifier</value> </set> </property> </bean>
AutowireCandidateResolver經過如下判斷注入條件:
當一個混合的beans的匹配符做爲自動裝配候選者,主要的候選者以下肯定:假如一個候選者bean定義中設置了primary屬性爲true,它就會被選中.
spring也支持使用@Resource註解來修飾字段或bean的屬性設置方法.這是java EE 5和6中的公共模式,例如JSF1.2中的被管理bean或JAX-WS2.0端點.spring也支持該模式.
@Resource 提供了一個name屬性,spring默認會攔截bean的名稱進行過濾.也就是說,它遵循經過名稱語法,就想下面例子所示:
public class SimpleMovieLister { private MovieFinder movieFinder; @Resource(name="myMovieFinder") public void setMovieFinder(MovieFinder movieFinder) { this.movieFinder = movieFinder; } }
若是名字沒有且明確指出,那麼默認名稱未來源於字段名稱或set方法.若是是字段,它取字段名稱;若是是set方法,它取設置的屬性名稱.因此如下的例子中將把名字爲'movieFinder'的bean注入到這個setter方法裏;
public class SimpleMovieLister { private MovieFinder movieFinder; @Resource public void setMovieFinder(MovieFinder movieFinder) { this.movieFinder = movieFinder; } }
######注 由@Resource註解提供的名字會經過CommonAnnotationBeanPostProcessor感知而被ApplicationContext處理爲bean的名稱.若是你明確的註冊了SimpleJndiBeanFactory,這些名字會由JNDI進行處理.可是,提醒一下,你最好依賴容器默認行爲並少使用spring的JNDI查找能力,這樣能夠保持間接的水平.
若是@Resource沒有指定具體的名稱,用法同@Autowired相同,@Resource會進行類型匹配以替代適配符匹配並用來解決一些經常使用的依賴:beanFactory,ApplicationContext,ResourceLoader,ApplicationEventPublisher,MessageSource接口.
下面的例子中,customerPerferenceDao字段首先查找一個名爲customPerferenceDao的bean,若是失敗則查看類型是否匹配CusstomerPreferencceDao.這個"context"字段將依據已知的依賴類型ApplicationContext進行注入.
public class MovieRecommender { @Resource private CustomerPreferenceDao customerPreferenceDao; @Resource private ApplicationContext context; public MovieRecommender() { } // ... }
####7.9.8 @PostConstruct and @PreDestroy 構造以後,銷燬以前 CommonAnnotationBeanPostProcessor不只識別@Resourece註解,也識別JSR-250中生命週期註解.在spring2.5中引入的,對這些註解的支持提供了初始化回調和銷燬回調時的其餘可選項.經過被spring ApplicationContext註冊的CommonAnnotationBeanPostProcessor,一個帶有一個或多個註解的方法能夠在什麼週期同一點上被調用,做爲spring的生命週期接口方法或顯示的申明回調方法.下面的例子中,這些緩存會再初始化以前操做,並在銷燬以後清理.
public class CachingMovieLister { @PostConstruct public void populateMovieCache() { // populates the movie cache upon initialization... } @PreDestroy public void clearMovieCache() { // clears the movie cache upon destruction... } }