?
This document uses PHP Chinese website manual Release
在第?3.7.1.2?節(jié) “RequiredAnnotationBeanPostProcessor
示例”一節(jié)中我們提到了基于注解的配置方式,使用BeanPostProcessor
與注解是 Spring IoC 容器的一個(gè)普通擴(kuò)展方法。例如,Spring 2.0 對(duì)必須的屬性引入了@Required注解。在 Spring 2.5中已經(jīng)可以用注解的方式去驅(qū)動(dòng) Spring 的依賴注射了。更重要的是,@Autowired
注解提供了與第?3.3.5?節(jié) “自動(dòng)裝配(autowire)協(xié)作者”一節(jié)中描述的同樣功能,并且提供了更細(xì)致的控制與更好的適應(yīng)性。Spring 2.5 也支持 JSR-250 中的一些注解,例如@Resource
,@PostConstruct
,以及@PreDestroy
。當(dāng)然,要使注解可用,您必須使用 Java 5 (Tiger)或更新的版本,以使得可以訪問(wèn)源代碼層次的注解。這些注解可以被注冊(cè)為獨(dú)立 bean 的定義,但它們也可以被隱式地注冊(cè),通過(guò)基于 XML 的配置方式,如下例(請(qǐng)注意包含 'context
' 命名空間):
<?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-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd"> <context:annotation-config/> </beans>
(隱式注冊(cè) post-processors 包括了 AutowiredAnnotationBeanPostProcessor
,CommonAnnotationBeanPostProcessor
,PersistenceAnnotationBeanPostProcessor
,也包括了前面提到的 RequiredAnnotationBeanPostProcessor
。)
@Autowired
注解可以用于“傳統(tǒng)的”setter 方法,如下例:
public class SimpleMovieLister {
private MovieFinder movieFinder;
@Autowired
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
// ...
}
這個(gè)注解也可以用于以屬性為參數(shù)/多個(gè)參數(shù)的方法
public class MovieRecommender {
private MovieCatalog movieCatalog;
private CustomerPreferenceDao customerPreferenceDao;
@Autowired
public void prepare(MovieCatalog movieCatalog, CustomerPreferenceDao customerPreferenceDao) {
this.movieCatalog = movieCatalog;
this.customerPreferenceDao = customerPreferenceDao;
}
// ...
}
@Autowired
注解甚至可以用于構(gòu)造器與字段:
public class MovieRecommender {
@Autowired
private MovieCatalog movieCatalog;
private CustomerPreferenceDao customerPreferenceDao;
@Autowired
public MovieRecommender(CustomerPreferenceDao customerPreferenceDao) {
this.customerPreferenceDao = customerPreferenceDao;
}
// ...
}
也可以一種提供來(lái)自ApplicationContext
的特殊類型的所有 beans,注解字段或者方法,例如:
public class MovieRecommender {
@Autowired
private MovieCatalog[] movieCatalogs;
// ...
}
這同樣適用于集合類型:
public class MovieRecommender {
private Set<MovieCatalog> movieCatalogs;
@Autowired
public void setMovieCatalogs(Set<MovieCatalog> movieCatalogs) {
this.movieCatalogs = movieCatalogs;
}
// ...
}
甚至是 Maps 也可以這樣注解,只要這個(gè) Map 的 key 類型為 String
。這個(gè) Map 的 values 應(yīng)該是已知的類型,并且 keys 應(yīng)該包含符合 bean 的命名:
public class MovieRecommender {
private Map<String, MovieCatalog> movieCatalogs;
@Autowired
public void setMovieCatalogs(Map<String, MovieCatalog> movieCatalogs) {
this.movieCatalogs = movieCatalogs;
}
// ...
}
在缺省情況下,當(dāng)出現(xiàn)0個(gè)候選的 beans時(shí)自動(dòng)連接將失敗;缺省行為把連接方法,構(gòu)造器,字段假設(shè)為 required 的依賴。這樣的行為如下所示:
public class SimpleMovieLister {
private MovieFinder movieFinder;
@Autowired(required=false)
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
// ...
}
雖然當(dāng) 一個(gè)類只有一個(gè)連接構(gòu)造器時(shí)它將被標(biāo)記為 required, 但是還是可以標(biāo)記多個(gè)構(gòu)造器的。在這種情況下,每一個(gè)構(gòu)造器都有可能被認(rèn)為是連接構(gòu)造器, Spring 將會(huì)把依賴關(guān)系能夠滿足的構(gòu)造器認(rèn)為是greediest 的構(gòu)造器。
@Autowired
也能用于總所周知的“可解決的依賴”:BeanFactory
接口,ApplicationContext
接口,ResourceLoader
接口,ApplicationEventPublisher
接口,還有MessageSource
接口。這些接口(還有它們的擴(kuò)展,例如ConfigurableApplicationContext
或者ResourcePatternResolver
)將可以自動(dòng)解決依賴,沒(méi)有任何特殊必須的其它步驟需要。
public class MovieRecommender {
@Autowired
private ApplicationContext context;
public MovieRecommender() {
}
// ...
}
因?yàn)橥ㄟ^(guò)類型的自動(dòng)連接可能會(huì)有多個(gè)候選,因此經(jīng)常需要在選擇過(guò)程中加以控制。一種方法去完成這個(gè)控制就是使用@Qualifier
注解。在最簡(jiǎn)單的情況下,您能夠通過(guò)命名方式去實(shí)現(xiàn)這個(gè)自動(dòng)連接:
public class MovieRecommender { @Autowired @Qualifier("mainCatalog") private MovieCatalog movieCatalog; // ... }
@Qualifier
注解也能夠被指定為構(gòu)造器的參數(shù)或者方法的參數(shù):
public class MovieRecommender { private MovieCatalog movieCatalog; private CustomerPreferenceDao customerPreferenceDao; @Autowired public void prepare(@Qualifier("mainCatalog") MovieCatalog movieCatalog, CustomerPreferenceDao customerPreferenceDao) { this.movieCatalog = movieCatalog; this.customerPreferenceDao = customerPreferenceDao; } // ... }
您也可以創(chuàng)建您自定義的限定器注解。您只要在定義一個(gè)注解時(shí)提供@Qualifier
注解就可以了:
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Genre {
String value();
}
然后您就能夠?qū)⑦@個(gè)自定義的限定器與參數(shù)用于自動(dòng)連接的字段:
public class MovieRecommender { @Autowired @Genre("Action") private MovieCatalog actionCatalog; private MovieCatalog comedyCatalog; @Autowired public void setComedyCatalog(@Genre("Comedy") MovieCatalog comedyCatalog) { this.comedyCatalog = comedyCatalog; } // ... }
下一步就是提供信息給候選的 bean 的定義。您能夠添加<qualifier/>
標(biāo)簽作為<bean/>
標(biāo)簽的子元素,然后指定'type'
還有'value'
以匹配您的自定義限定器注解。類型必須匹配注解的全名,或者是一個(gè)不危險(xiǎn)的、方便一點(diǎn)的名字,您也可以使用“短” 類名。參看下例:
<?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-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.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>
在下一節(jié),題目是第?3.12?節(jié) “對(duì)受管組件的Classpath掃描”,您將看到使用XML提供給限定器元數(shù)據(jù)且基于注解的可選解決方案。特別地,請(qǐng)參看:第?3.12.6?節(jié) “用注解提供限定符元數(shù)據(jù)”。
在某些情況下,有足夠充分的理由去使用不帶值的注解。這使得注解可以提供更多解決不同類型依賴的能力。例如,在 Internet 連接不可用時(shí),您可以提供一個(gè)離線的搜索目錄。首先就要定義一個(gè)簡(jiǎn)單的注解:
@Target({ElementType.FIELD, ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) @Qualifier public @interface Offline { }
然后添加這個(gè)注解給字段作為自動(dòng)連接:
public class MovieRecommender { @Autowired @Offline private MovieCatalog offlineCatalog; // ... }
現(xiàn)在,這個(gè) bean 的定影只組要一個(gè)限定器了:
<bean class="example.SimpleMovieCatalog"> <qualifier type="Offline"/> <!-- inject any dependencies required by this bean --> </bean>
另外,也可以定制自己的限定器注解去使用命名的屬性或者簡(jiǎn)單的'value'
屬性。如果自動(dòng)連接時(shí)多個(gè)屬性值被指定給了一個(gè)字段或者參數(shù),那么一個(gè) bean 的定義必須全部匹配這些屬性的值。例如,考慮如下的注解定義:
@Target({ElementType.FIELD, ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) @Qualifier public @interface MovieQualifier { String genre(); Format format(); }
在這種情況下,Format
是一個(gè)枚舉:
public enum Format { VHS, DVD, BLURAY }
這些字段將與自定義的限定器進(jìn)行自動(dòng)連接,包括了每個(gè)屬性的值:'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;
// ...
}
最終,這個(gè) bean 的定義應(yīng)該與限定器匹配的值。這個(gè)列子將說(shuō)明bean 的元屬性可以用于替代<qualifier/>
的子元素。那樣的話,<qualifier/>
以及它的屬性將優(yōu)先考慮,但是如果沒(méi)有限定器的話(參看如下定義的后兩個(gè) bean ),自動(dòng)連接機(jī)制將取消以<meta/>
標(biāo)簽標(biāo)記的值。
<?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-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd"> <context:annotation-config/> <bean class="example.SimpleMovieCatalog"> <qualifier type="MovieQualifier"> <attribute name="format" value="VHS"/> <attribute name="genre" value="Action"/> </qualifier> <!-- inject any dependencies required by this bean --> </bean> <bean class="example.SimpleMovieCatalog"> <qualifier type="MovieQualifier"> <attribute name="format" value="VHS"/> <attribute name="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>
CustomAutowireConfigurer
是一個(gè)BeanFactoryPostProcessor
,它可以使得在自動(dòng)連接過(guò)程中做更多的自定義選擇。特殊地,它允許您注冊(cè)您自己的自定義限定器注解類型,甚至是它們沒(méi)有使用 Spring 的@Qualifier
注解標(biāo)注它們自己。
<bean id="customAutowireConfigurer" class="org.springframework.beans.factory.annotation.CustomAutowireConfigurer"> <property name="customQualifierTypes"> <set> <value>example.CustomQualifier</value> </set> </property> </bean>
請(qǐng)注意,AutowireCandidateResolver
的實(shí)現(xiàn)將依賴于 Java 版本。如果在 Java 5 以下,限定器注解是不被支持的,因此自動(dòng)連接后選將被'autowire-candidate'
的值或者在<beans/>
中元素'default-autowire-candidates'
可用的模式所決定。如果運(yùn)行在 Java 5 或者更新的版本上,@Qualifier
注解或者任何自定義并在CustomAutowireConfigurer
上注冊(cè)過(guò)的注解都將正常工作。
忽略 Java 版本,決定“主要”的后選(當(dāng)多個(gè) beans 都配置為自動(dòng)連接后選時(shí))都是一樣的:在這些后選中只要一個(gè) bean 的'primary'
屬性定義為'true'
即可。
Spring 也提供了使用 JSR-250 bean 屬性支持的注射方式。這是一種在 Java EE 5 與 Java 6 中普遍使用的方式(例如,在 JSF 1.2 中映射 beans 或者 JAX-WS 2.0 端點(diǎn)),對(duì)于Spring 托管的對(duì)象 Spring 可以以這種方式支持映射。
@Resource
有一個(gè)‘name’屬性,缺省時(shí),Spring 將這個(gè)值解釋為要注射的 bean 的名字。換句話說(shuō),如果遵循by-name的語(yǔ)法,如下例:
public class SimpleMovieLister {
private MovieFinder movieFinder;
@Resource(name="myMovieFinder")
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
}
如果沒(méi)有顯式地給出名字,缺省的名字將繼承于字段名或者 setter 方法名:如果是字段名,它將簡(jiǎn)化或者等價(jià)于字段名;如果是 setter 方法名,它將等價(jià)于 bean 屬性名。下面這個(gè)例子使用名字 "movieFinder" 注射到它的 setter 方法:
public class SimpleMovieLister {
private MovieFinder movieFinder;
@Resource
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
}
注解提供的名字將被BeanFactory
解析為 bean 名。請(qǐng)注意,這些名字也可能通過(guò) JNDI 被解析(需要配置 Spring 的SimpleJndiBeanFactory
)。不過(guò),建議您依靠缺省行為與 Spring 的 JNDI 查找功能。
與@Autowired
類似,@Resource
可以回退為與標(biāo)準(zhǔn) bean 類型匹配(例如,使用原始類型匹配取代特殊命名 bean)來(lái)解決著名的"resolvable dependencies":BeanFactory
接口,ApplicationContext
接口,ResourceLoader
接口,ApplicationEventPublisher
接口以及 MessageSource
接口。請(qǐng)注意:這只有適用于未指定命名的@Resource
!
下面的例子有一個(gè)customerPreferenceDao
字段,首先要查找一個(gè)名叫 “customerPreferenceDao” 的 bean,然后回退為一個(gè)原始類型以匹配類型CustomerPreferenceDao
。"context" 字段將基于已知解決的依賴類型ApplicationContext
而被注入。
public class MovieRecommender {
@Resource
private CustomerPreferenceDao customerPreferenceDao;
@Resource
private ApplicationContext context;
public MovieRecommender() {
}
// ...
}
CommonAnnotationBeanPostProcessor
不只是能識(shí)別@Resource
注解,而且也能識(shí)別 JSR-250 lifecycle注解。在 Spring 2.5 中,這些注解的描述請(qǐng)參看initialization callbacks 與 destruction callbacks節(jié)。CommonAnnotationBeanPostProcessor
已經(jīng)在 Spring 的ApplicationContext
中注冊(cè),當(dāng)一個(gè)方法帶有這些注解之一時(shí),將被在其生命周期與 Spring 生命周期接口的方法或者顯式聲明回調(diào)方法同一刻上調(diào)用。下面的例子里,緩存將預(yù)置于初始化與銷(xiāo)毀階段。
public class CachingMovieLister { @PostConstruct public void populateMovieCache() { // populates the movie cache upon initialization... } @PreDestroy public void clearMovieCache() { // clears the movie cache upon destruction... } }
關(guān)于組合不同的生命周期機(jī)制,請(qǐng)查看第?3.5.1.4?節(jié) “組合生命周期機(jī)制”。