Spring 5.0.0框架介紹_中文版_3.9

文章作者:Tyan
博客:noahsnail.com ?|? CSDN ?|? 簡(jiǎn)書(shū)

3.9 基于注解的容器配置

在配置Spring時(shí)注解是否比XML更好兰粉?

基于注解配置的引入引出了一個(gè)問(wèn)題——這種方式是否比基于XML的配置更好。簡(jiǎn)短的回答是視情況而定外邓。長(zhǎng)一點(diǎn)的回答是每種方法都有它的優(yōu)點(diǎn)和缺點(diǎn)戚篙,通常是由開(kāi)發(fā)者決定哪一種策略更適合他們痊土。由于注解的定義方式塞帐,注解在它們的聲明中提供了許多上下文单旁,導(dǎo)致配置更簡(jiǎn)短更簡(jiǎn)潔沪羔。然而,XML擅長(zhǎng)連接組件而不必接觸源代碼或重新編譯它們象浑。一些開(kāi)發(fā)者更喜歡接近源代碼蔫饰,而另一些人則認(rèn)為基于注解的類(lèi)不再是POJOs,此外愉豺,配置變的去中心化篓吁,而且更難控制。

無(wú)論選擇是什么蚪拦,Spring都能容納這兩種風(fēng)格杖剪,甚至可以將它們混合在一起。值得指出的是驰贷,通過(guò)它的Java配置選項(xiàng)盛嘿,Spring允許注解以一種非入侵的方式使用,不觸碰目標(biāo)組件源碼和那些工具括袒,所有的配置風(fēng)格由Spring工具套件支持次兆。

基于注解的配置提供了一種XML設(shè)置的可替代方式,它依賴于字節(jié)碼元數(shù)據(jù)來(lái)連接組件锹锰,而不是用尖括號(hào)聲明的方式芥炭。代替使用XML來(lái)描述bean連接,開(kāi)發(fā)者通過(guò)將注解使用在相關(guān)的類(lèi)恃慧,方法或字段聲明中园蝠,將配置移動(dòng)到了組件類(lèi)本身的內(nèi)部。正如在“Example: The RequiredAnnotationBeanPostProcessor”那節(jié)提到的那樣痢士,使用BeanPostProcessor與注解結(jié)合是擴(kuò)展Spring IoC容器的的常見(jiàn)方法彪薛。例如,Spring 2.0引入了@Required注解來(lái)執(zhí)行需要的屬性的可能性良瞧。Spring 2.5使以同樣地通用方法來(lái)驅(qū)動(dòng)Spring的依賴注入變?yōu)榭赡芘闫1举|(zhì)上來(lái)說(shuō),@Autowired提供了如3.4.5小節(jié)描述的同樣的能力褥蚯≈吭“Autowiring collaborators”但更細(xì)粒度的控制和更廣的應(yīng)用性。Spring 2.5也添加對(duì)JSR-250注解的支持赞庶,例如训挡,@PostConstruct@PreDestroy
澳骤。Spring 3.0添加了對(duì)JSR-330,包含在javax.inject包內(nèi)的注解(Java的依賴注入)的支持澜薄,例如@Inject@Named为肮。關(guān)于這些注解的細(xì)節(jié)可以在相關(guān)的小節(jié)找到。

注解注入在XML注入之前進(jìn)行肤京,因此對(duì)于通過(guò)兩種方法進(jìn)行組裝的屬性后者的配置會(huì)覆蓋前者颊艳。

跟以前一樣,你可以作為單獨(dú)的bean定義來(lái)注冊(cè)它們忘分,但也可以通過(guò)在一個(gè)基于XML的Spring配置(注入包含上下文命名空間)中包含下面的標(biāo)簽來(lái)隱式的注冊(cè)它們:

<?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>

(隱式注冊(cè)的后處理器包括 AutowiredAnnotationBeanPostProcessor棋枕,CommonAnnotationBeanPostProcessorPersistenceAnnotationBeanPostProcessor和前面提到的RequiredAnnotationBeanPostProcessor妒峦。)

<context:annotation-config/>僅在定義它的同樣的應(yīng)用上下文中尋找注解的beans重斑。這意味著,如果你在一個(gè)為DispatcherServlet服務(wù)的WebApplicationContext中放置了<context:annotation-config/>肯骇,它只能在你的控制器中尋找@Autowired注解的beans窥浪,而不是在你的服務(wù)層中。更多信息請(qǐng)看18.2小節(jié)笛丙,“The DispatcherServlet”漾脂。

3.9.1 @Required

@Required注解應(yīng)用到bean屬性的setter方法上,例子如下:

public class SimpleMovieLister {

    private MovieFinder movieFinder;

    @Required
    public void setMovieFinder(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }

    // ...

}

這個(gè)注解僅僅是表明受影響的bean屬性必須在配置時(shí)通過(guò)顯式的bean定義或自動(dòng)組裝填充胚鸯。如果受影響的bean屬性沒(méi)有填充符相,容器會(huì)拋出一個(gè)異常,這允許及早明確的失敗蠢琳,避免NullPointerExceptions或后面出現(xiàn)類(lèi)似的情況。仍然建議你在bean類(lèi)本身加入斷言镜豹,例如傲须,加入到初始化方法中。這樣做可以強(qiáng)制這些需要的引用和值趟脂,甚至是你在容器外部使用這個(gè)類(lèi)的時(shí)候泰讽。

3.9.2 @Autowired

在下面的例子中JSR 330的@Inject注解可以用來(lái)代替Spring的@Autowired注解。

你可以將@Autowired注解應(yīng)用到構(gòu)造函數(shù)上昔期。

public class MovieRecommender {

    private final CustomerPreferenceDao customerPreferenceDao;

    @Autowired
    public MovieRecommender(CustomerPreferenceDao customerPreferenceDao) {
        this.customerPreferenceDao = customerPreferenceDao;
    }

    // ...

}

從Spring框架4.3起已卸,如果目標(biāo)bena僅定義了一個(gè)構(gòu)造函數(shù),那么@Autowired注解的構(gòu)造函數(shù)不再是必要的硼一。如果一些構(gòu)造函數(shù)是可獲得的累澡,至少有一個(gè)必須要加上注解,以便于告訴容器使用哪一個(gè)般贼。

正如預(yù)料的那樣愧哟,你也可以將@Autowired注解應(yīng)用到“傳統(tǒng)的”setter方法上:

public class SimpleMovieLister {

    private MovieFinder movieFinder;

    @Autowired
    public void setMovieFinder(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }

    // ...

}

你也可以應(yīng)用注解到具有任何名字和/或多個(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;
    }

    // ...

}

你也可以應(yīng)用@Autowired到字段上奥吩,甚至可以與構(gòu)造函數(shù)混合用:

public class MovieRecommender {

    private final CustomerPreferenceDao customerPreferenceDao;

    @Autowired
    private MovieCatalog movieCatalog;

    @Autowired
    public MovieRecommender(CustomerPreferenceDao customerPreferenceDao) {
        this.customerPreferenceDao = customerPreferenceDao;
    }

    // ...

}

通過(guò)給帶有數(shù)組的字段或方法添加@Autowired注解,也可以從ApplicationContext中提供一組特定類(lèi)型的bean:

public class MovieRecommender {

    @Autowired
    private MovieCatalog[] movieCatalogs;

    // ...

}

同樣也可以應(yīng)用到具有同一類(lèi)型的集合上:

public class MovieRecommender {

    private Set<MovieCatalog> movieCatalogs;

    @Autowired
    public void setMovieCatalogs(Set<MovieCatalog> movieCatalogs) {
        this.movieCatalogs = movieCatalogs;
    }

    // ...

}

如果你希望數(shù)組或列表中的項(xiàng)按指定順序排序蕊梧,你的bean可以實(shí)現(xiàn)org.springframework.core.Ordered接口霞赫,或使用@Order或標(biāo)準(zhǔn)@Priority注解。

只要期望的key是String肥矢,那么類(lèi)型化的Maps就可以自動(dòng)組裝端衰。Map的值將包含所有期望類(lèi)型的beans,key將包含對(duì)應(yīng)的bean名字:

public class MovieRecommender {

    private Map<String, MovieCatalog> movieCatalogs;

    @Autowired
    public void setMovieCatalogs(Map<String, MovieCatalog> movieCatalogs) {
        this.movieCatalogs = movieCatalogs;
    }

    // ...

}

默認(rèn)情況下甘改,當(dāng)沒(méi)有候選beans可獲得時(shí)旅东,自動(dòng)組裝會(huì)失敗楼誓;默認(rèn)的行為是將注解的方法玉锌,構(gòu)造函數(shù)和字段看作指明了需要的依賴。這個(gè)行為也可以通過(guò)下面的方式去改變疟羹。

public class SimpleMovieLister {

    private MovieFinder movieFinder;

    @Autowired(required=false)
    public void setMovieFinder(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }

    // ...

}

每個(gè)類(lèi)只有一個(gè)構(gòu)造函數(shù)可以標(biāo)記為必需的主守,但可以注解多個(gè)非必需的構(gòu)造函數(shù)。在這種情況下榄融,會(huì)考慮這些候選者中的每一個(gè)参淫,Spring使用最貪婪的構(gòu)造函數(shù),即依賴最滿足的構(gòu)造函數(shù)愧杯,具有最大數(shù)目的參數(shù)涎才。

建議在@Required注解之上使用@Autowiredrequired特性。required特性表明這個(gè)屬性自動(dòng)裝配是不需要的力九,如果這個(gè)屬性不能被自動(dòng)裝配耍铜,它會(huì)被忽略。另一方面@Required是更強(qiáng)大的跌前,在它強(qiáng)制這個(gè)屬性被任何容器支持的bean設(shè)置棕兼。如果沒(méi)有值注入,會(huì)拋出對(duì)應(yīng)的異常抵乓。

你也可以對(duì)那些已知的具有可解析依賴的接口使用@AutowiredBeanFactory伴挚,ApplicationContextEnvironment, ResourceLoader灾炭,ApplicationEventPublisherMessageSource茎芋。這些接口和它們的擴(kuò)展接口,例如ConfigurableApplicationContextResourcePatternResolver蜈出,可以自動(dòng)解析田弥,不需要特別的設(shè)置。

public class MovieRecommender {

    @Autowired
    private ApplicationContext context;

    public MovieRecommender() {
    }

    // ...

}

@Autowired铡原,@Inject皱蹦,@Resource@Value注解是通過(guò)Spring BeanPostProcessor實(shí)現(xiàn)處理煤杀,這反過(guò)來(lái)意味著你不能在你自己的BeanPostProcessorBeanFactoryPostProcessor中應(yīng)用這些注解(如果有的話)。這些類(lèi)型必須顯式的通過(guò)XML或使用Spring的@Bean方法來(lái)'wired up'沪哺。

3.9.3 用@Primary微調(diào)基于注解的自動(dòng)裝配

因?yàn)楦鶕?jù)類(lèi)型的自動(dòng)裝配可能會(huì)導(dǎo)致多個(gè)候選目標(biāo)沈自,所以在選擇過(guò)程中進(jìn)行更多的控制經(jīng)常是有必要的。一種方式通過(guò)Spring的@Primary注解來(lái)完成辜妓。當(dāng)有個(gè)多個(gè)候選bean要組裝到一個(gè)單值的依賴時(shí)枯途,@Primary表明指定的bean應(yīng)該具有更高的優(yōu)先級(jí)。如果確定一個(gè)'primary' bean位于候選目標(biāo)中間籍滴,它將是那個(gè)自動(dòng)裝配的值酪夷。

假設(shè)我們具有如下配置,將firstMovieCatalog定義為主要的MovieCatalog孽惰。

@Configuration
public class MovieConfiguration {

    @Bean
    @Primary
    public MovieCatalog firstMovieCatalog() { ... }

    @Bean
    public MovieCatalog secondMovieCatalog() { ... }

    // ...

}

根據(jù)這樣的配置晚岭,下面的MovieRecommender將用firstMovieCatalog進(jìn)行自動(dòng)裝配。

public class MovieRecommender {

    @Autowired
    private MovieCatalog movieCatalog;

    // ...

}

對(duì)應(yīng)的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" 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>

3.9.4 微調(diào)基于注解且?guī)в邢薅ǚ淖詣?dòng)裝配

當(dāng)有多個(gè)實(shí)例需要確定一個(gè)主要的候選對(duì)象時(shí)勋功,@Primary是一種按類(lèi)型自動(dòng)裝配的有效方式坦报。當(dāng)需要在選擇過(guò)程中進(jìn)行更多的控制時(shí),可以使用Spring的@Qualifier注解狂鞋。為了給每個(gè)選擇一個(gè)特定的bean片择,你可以將限定符的值與特定的參數(shù)聯(lián)系在一起,減少類(lèi)型匹配集合骚揍。在最簡(jiǎn)單的情況下字管,這是一個(gè)純描述性值:

public class MovieRecommender {

    @Autowired
    @Qualifier("main")
    private MovieCatalog movieCatalog;

    // ...

}

@Qualifier注解也可以指定單個(gè)構(gòu)造函數(shù)參數(shù)或方法參數(shù):

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;
    }

    // ...

}

對(duì)應(yīng)的bean定義如下。限定符值為"main"的bean被組裝到有相同值的構(gòu)造函數(shù)參數(shù)中信不。

<?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>

對(duì)于回退匹配嘲叔,bean名字被認(rèn)為是默認(rèn)的限定符值。因此你可以定義一個(gè)id為main的bean來(lái)代替內(nèi)嵌的限定符元素抽活,會(huì)有同樣的匹配結(jié)果借跪。然而,盡管你可以使用這個(gè)約定根據(jù)名字引用特定的beans酌壕,但是@Autowired從根本上來(lái)講是使用可選的語(yǔ)義限定符來(lái)進(jìn)行類(lèi)型驅(qū)動(dòng)注入的。這意味著限定符的值歇由,即使回退到bean名稱卵牍,總是縮小語(yǔ)義類(lèi)型匹配的集合;它們沒(méi)有從語(yǔ)義上將一個(gè)引用表達(dá)為一個(gè)唯一的bean id沦泌。好的限定符值是"main"或"EMEA"或"persistent"糊昙,表達(dá)一個(gè)特定組件的性質(zhì),這個(gè)組件是獨(dú)立于bean id的谢谦,即使前面例子中像這個(gè)bean一樣的匿名bean會(huì)自動(dòng)生成id释牺。

正如前面討論的那樣萝衩,限定符也可以應(yīng)用到類(lèi)型結(jié)合上,例如没咙,Set<MovieCatalog>猩谊。在這個(gè)例子中,根據(jù)聲明的限定符匹配的所有beans作為一個(gè)集合進(jìn)行注入祭刚。這意味著限定符不必是唯一的牌捷;它們只是構(gòu)成過(guò)濾標(biāo)準(zhǔn)。例如涡驮,你可以定義多個(gè)具有同樣限定符值"action"的MovieCatalog暗甥,所有的這些都將注入到帶有注解@Qualifier("action")Set<MovieCatalog>中。

如果你想通過(guò)名字表達(dá)注解驅(qū)動(dòng)的注入捉捅,不要主要使用@Autowired撤防,雖然在技術(shù)上能通過(guò)@Qualifier值引用一個(gè)bean名字。作為可替代產(chǎn)品棒口,可以使用JSR-250 @Resource注解寄月,它在語(yǔ)義上被定義為通過(guò)組件唯一的名字來(lái)識(shí)別特定的目標(biāo)組件,聲明的類(lèi)型與匹配過(guò)程無(wú)關(guān)陌凳。@Autowired有不同的語(yǔ)義:通過(guò)類(lèi)型選擇候選beans剥懒,特定的String限定符值被認(rèn)為只在類(lèi)型選擇的候選目標(biāo)中,例如合敦,在那些標(biāo)記為具有相同限定符標(biāo)簽的beans中匹配一個(gè)"account"限定符初橘。

對(duì)于那些本身定義在集合/映射或數(shù)組類(lèi)型中的beans來(lái)說(shuō),@Resource是一個(gè)很好的解決方案充岛,適用于特定的集合或通過(guò)唯一名字區(qū)分的數(shù)組bean保檐。也就是說(shuō),自Spring 4.3起崔梗,集合/映射和數(shù)組類(lèi)型中也可以通過(guò)Spring的@Autowired類(lèi)型匹配算法進(jìn)行匹配夜只,只要元素類(lèi)型信息在@Bean中保留,返回類(lèi)型簽名或集合繼承體系蒜魄。在這種情況下扔亥,限定符值可以用來(lái)在相同類(lèi)型的集合中選擇,正如在前一段中概括的那樣谈为。

自Spring 4.3起旅挤,@Autowired也考慮自引用注入,例如伞鲫,引用返回當(dāng)前注入的bean粘茄。注意自注入是備用;普通對(duì)其它組件的依賴關(guān)系總是優(yōu)先的。在這個(gè)意義上柒瓣,自引用不參與普通的候選目標(biāo)選擇儒搭,因此尤其是從不是主要的;恰恰相反芙贫,它們最終總是最低的優(yōu)先級(jí)搂鲫。在實(shí)踐中,自引用只是作為最后的手段屹培,例如默穴,通過(guò)bean的事務(wù)代理調(diào)用同一實(shí)例的其它方法:在考慮抽出受影響的方法來(lái)分隔代理bean的場(chǎng)景中⊥市悖或者蓄诽,使用@Resource通過(guò)它的唯一名字可能得到一個(gè)返回當(dāng)前bean的代理。

@Autowired可以應(yīng)用到字段媒吗,構(gòu)造函數(shù)和多參數(shù)方法上仑氛,允許通過(guò)限定符注解在參數(shù)層面上縮減候選目標(biāo)。相比之下,@Resource僅支持字段和bean屬性的帶有單個(gè)參數(shù)的setter方法。因此抹腿,如果你的注入目標(biāo)是一個(gè)構(gòu)造函數(shù)或一個(gè)多參數(shù)的方法赂苗,堅(jiān)持使用限定符账月。

你可以創(chuàng)建自己的定制限定符注解。簡(jiǎn)單定義一個(gè)注解,在你自己的定義中提供@Qualifier注解:

@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Genre {

    String value();
}

然后你可以在自動(dòng)裝配的字段和參數(shù)上提供定制的限定符:

public class MovieRecommender {

    @Autowired
    @Genre("Action")
    private MovieCatalog actionCatalog;
    private MovieCatalog comedyCatalog;

    @Autowired
    public void setComedyCatalog(@Genre("Comedy") MovieCatalog comedyCatalog) {
        this.comedyCatalog = comedyCatalog;
    }

    // ...

}

接下來(lái),提供候選bean定義的信息捶牢。你可以添加<qualifier/>標(biāo)記作為<bean/>標(biāo)記的子元素,然后指定匹配你的定制限定符注解的類(lèi)型和值巍耗。類(lèi)型用來(lái)匹配注解的全限定類(lèi)名稱秋麸。或者炬太,如果沒(méi)有名稱沖突的風(fēng)險(xiǎn)灸蟆,為了方便,你可以使用簡(jiǎn)寫(xiě)的類(lèi)名稱亲族。下面的例子證實(shí)了這些方法炒考。

<?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>

在3.10小節(jié),“類(lèi)路徑掃描和管理組件”中霎迫,你將看到一個(gè)基于注解的替代方法斋枢,在XML中提供限定符元數(shù)據(jù)。特別地女气,看3.10.8小節(jié),“用注解提供限定符元數(shù)據(jù)”测柠。

在某些情況下炼鞠,使用沒(méi)有值的注解就是足夠的缘滥。當(dāng)注解為了通用的目的時(shí),這是非常有用的谒主,可以應(yīng)用到跨幾個(gè)不同類(lèi)型的依賴上朝扼。例如,當(dāng)網(wǎng)絡(luò)不可用時(shí)霎肯,你可以提供一個(gè)要搜索的離線目錄擎颖。首先定義一個(gè)簡(jiǎn)單的注解:

@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Offline {

}

然后將注解添加到要自動(dòng)裝配的字段或?qū)傩陨希?/p>

public class MovieRecommender {

    @Autowired
    @Offline
    private MovieCatalog offlineCatalog;

    // ...

}

現(xiàn)在bean定義只需要一個(gè)限定符類(lèi)型:

<bean class="example.SimpleMovieCatalog">
    <qualifier type="Offline"/>
    <!-- inject any dependencies required by this bean -->
</bean>

你也可以定義接收命名屬性之外的定制限定符注解或代替簡(jiǎn)單的值屬性。如果要注入的字段或參數(shù)指定了多個(gè)屬性值观游,bean定義必須匹配所有的屬性值才會(huì)被認(rèn)為是一個(gè)可自動(dòng)裝配的候選目標(biāo)搂捧。作為一個(gè)例子,考慮下面的注解定義:

@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface MovieQualifier {

    String genre();

    Format format();

}

這種情況下Format是枚舉類(lèi)型:

public enum Format {
    VHS, DVD, BLURAY
}

要自動(dòng)裝配的字段使用定制限定符進(jìn)行注解懂缕,并且包含了兩個(gè)屬性值:genreformat允跑。

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定義應(yīng)該包含匹配的限定符值搪柑。這個(gè)例子也證實(shí)了bean元屬性可以用來(lái)代替<qualifier/>子元素聋丝。如果可獲得<qualifier/>,它和它的屬性優(yōu)先級(jí)更高工碾,如果當(dāng)前沒(méi)有限定符弱睦,自動(dòng)裝配機(jī)制會(huì)將<meta/>內(nèi)的值作為備用,正如下面的例子中的最后兩個(gè)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>

3.9.5 使用泛型作為自動(dòng)裝配限定符

除了@Qualifier注解外况木,也可以使用Java的泛型類(lèi)型作為限定符的一種暗示方式。例如端圈,假設(shè)你有如下配置:

@Configuration
public class MyConfiguration {

    @Bean
    public StringStore stringStore() {
        return new StringStore();
    }

    @Bean
    public IntegerStore integerStore() {
        return new IntegerStore();
    }

}

假設(shè)上面的beans實(shí)現(xiàn)了一個(gè)泛型接口焦读,例如,Store<String>Store<Integer>舱权,你可以@Autowire Store接口矗晃,泛型將作為限定符使用:

@Autowired
private Store<String> s1; // <String> qualifier, injects the stringStore bean

@Autowired
private Store<Integer> s2; // <Integer> qualifier, injects the integerStore bean

當(dāng)自動(dòng)裝配ListsMapsArrays時(shí)宴倍,也會(huì)應(yīng)用泛型限定符:

// 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;

3.9.6 CustomAutowireConfigurer

CustomAutowireConfigurer是一個(gè)能使你注冊(cè)自己的定制限定符注解類(lèi)型的BeanFactoryPostProcessor张症,即使它們不使用Spring的@Qualifier注解進(jìn)行注解。

<bean id="customAutowireConfigurer"
        class="org.springframework.beans.factory.annotation.CustomAutowireConfigurer">
    <property name="customQualifierTypes">
        <set>
            <value>example.CustomQualifier</value>
        </set>
    </property>
</bean>

AutowireCandidateResolver通過(guò)下面的方式?jīng)Q定自動(dòng)裝配的候選目標(biāo):

  • 每個(gè)bean定義的autowire-candidate

  • <beans/>元素可獲得的任何default-autowire-candidates模式

  • 存在@Qualifier注解和任何在CustomAutowireConfigurer中注冊(cè)的定制注解

當(dāng)多個(gè)beans符合條件成為自動(dòng)裝配的候選目標(biāo)時(shí)鸵贬,"primary" bean的決定如下:如果在候選目標(biāo)中某個(gè)確定的bean中的primary特性被設(shè)為true俗他,它將被選為目標(biāo)bean。

3.9.7 @Resource

Spring也支持使用JSR-250 @Resource對(duì)字段或bean屬性setter方法進(jìn)行注入阔逼。這是在Java EE 5和6中的一種通用模式兆衅,例如在JSF 1.2管理的beans或JAX-WS 2.0的端點(diǎn)。Spring對(duì)它管理的對(duì)象也支持這種模式。

@Resource采用名字屬性羡亩,默認(rèn)情況下Spring將名字值作為要注入的bean的名字摩疑。換句話說(shuō),它遵循by-name語(yǔ)義畏铆,下面的例子證實(shí)了這一點(diǎn):

public class SimpleMovieLister {

    private MovieFinder movieFinder;

    @Resource(name="myMovieFinder")
    public void setMovieFinder(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }

}

如果沒(méi)有顯式的指定名字雷袋,默認(rèn)名字從字段名或setter方法中取得。在字段情況下辞居,它采用字段名稱楷怒;在setter方法情況下,它采用bean的屬性名瓦灶。因此下面的例子將名字為movieFinder的bean注入到它的setter方法中:

public class SimpleMovieLister {

    private MovieFinder movieFinder;

    @Resource
    public void setMovieFinder(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }

}

注解提供的名字被CommonAnnotationBeanPostProcessor感知的ApplicationContext解析為bean名字鸠删。如果你顯式地配置了Spring的SimpleJndiBeanFactory,名字會(huì)通過(guò)JNDI解析倚搬。但是建議你依賴默認(rèn)行為冶共,簡(jiǎn)單使用Spring的JNDI查找功能保護(hù)間接查找級(jí)別。

@Resource特有的沒(méi)有顯式名字指定的情況下每界,類(lèi)似于@Autowired捅僵,@Resource會(huì)進(jìn)行主要的匹配類(lèi)型來(lái)代替指定名字的bean并解析已知的可解析依賴:BeanFactoryApplicationContext眨层,ResourceLoader庙楚,ApplicationEventPublisherMessageSource接口。

因此在下面的例子中趴樱,customerPreferenceDao字段首先查找名字為customerPreferenceDao的bean馒闷,然后回退到主要的類(lèi)型為CustomerPreferenceDao的類(lèi)型匹配。"context"字段會(huì)注入基于已知的可解析依賴類(lèi)型ApplicationContext叁征。

public class MovieRecommender {

    @Resource
    private CustomerPreferenceDao customerPreferenceDao;

    @Resource
    private ApplicationContext context;

    public MovieRecommender() {
    }

    // ...

}

3.9.8 @PostConstruct和@PreDestroy

CommonAnnotationBeanPostProcessor不僅識(shí)別@Resource注解纳账,而且識(shí)別JSR-250生命周期注解。在Spring 2.5引入了對(duì)這些注解的支持捺疼,也提供了在初始化回調(diào)函數(shù)和銷(xiāo)毀回調(diào)函數(shù)中描述的那些注解的一種可替代方式疏虫。假設(shè)CommonAnnotationBeanPostProcessor在Spring的ApplicationContext中注冊(cè),執(zhí)行這些注解的方法在生命周期的同一點(diǎn)被調(diào)用啤呼,作為對(duì)應(yīng)的Spring生命周期接口方法或顯式聲明的回調(diào)方法卧秘。在下面的例子中,緩存會(huì)預(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ī)制的影響的更多細(xì)節(jié),請(qǐng)看“組合生命周期機(jī)制”小節(jié)惕蹄。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末蚯涮,一起剝皮案震驚了整個(gè)濱河市治专,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌遭顶,老刑警劉巖看靠,帶你破解...
    沈念sama閱讀 217,277評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異液肌,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)鸥滨,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門(mén)嗦哆,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人婿滓,你說(shuō)我怎么就攤上這事老速。” “怎么了凸主?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,624評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵橘券,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我卿吐,道長(zhǎng)旁舰,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,356評(píng)論 1 293
  • 正文 為了忘掉前任嗡官,我火速辦了婚禮箭窜,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘衍腥。我一直安慰自己磺樱,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,402評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布婆咸。 她就那樣靜靜地躺著竹捉,像睡著了一般。 火紅的嫁衣襯著肌膚如雪尚骄。 梳的紋絲不亂的頭發(fā)上块差,一...
    開(kāi)封第一講書(shū)人閱讀 51,292評(píng)論 1 301
  • 那天,我揣著相機(jī)與錄音乖仇,去河邊找鬼憾儒。 笑死,一個(gè)胖子當(dāng)著我的面吹牛乃沙,可吹牛的內(nèi)容都是我干的起趾。 我是一名探鬼主播,決...
    沈念sama閱讀 40,135評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼警儒,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼训裆!你這毒婦竟也來(lái)了眶根?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 38,992評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤边琉,失蹤者是張志新(化名)和其女友劉穎属百,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體变姨,經(jīng)...
    沈念sama閱讀 45,429評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡族扰,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,636評(píng)論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了定欧。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片渔呵。...
    茶點(diǎn)故事閱讀 39,785評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖砍鸠,靈堂內(nèi)的尸體忽然破棺而出扩氢,到底是詐尸還是另有隱情,我是刑警寧澤爷辱,帶...
    沈念sama閱讀 35,492評(píng)論 5 345
  • 正文 年R本政府宣布录豺,位于F島的核電站,受9級(jí)特大地震影響饭弓,放射性物質(zhì)發(fā)生泄漏双饥。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,092評(píng)論 3 328
  • 文/蒙蒙 一弟断、第九天 我趴在偏房一處隱蔽的房頂上張望兢哭。 院中可真熱鬧,春花似錦夫嗓、人聲如沸迟螺。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,723評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)矩父。三九已至,卻和暖如春排霉,著一層夾襖步出監(jiān)牢的瞬間窍株,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,858評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工攻柠, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留球订,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,891評(píng)論 2 370
  • 正文 我出身青樓瑰钮,卻偏偏與公主長(zhǎng)得像冒滩,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子浪谴,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,713評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容