Spring官方文檔通讀-部分三

1.9 基於註釋的容器配置

基於註釋的配置的引入引起了這種方法是否比XML「更好」的問題。簡短的回答是「它取決於。」長期的答案是每種方法都有其優勢和缺點,一般,由開發人員決定哪一種策略更適合他們。因爲它們的定義方式,註釋在其聲明中提供了大量上下文,從而致使更短更簡潔的配置。可是,XML擅長在不觸及源代碼或從新編譯它們的狀況下鏈接組件。一些開發人員更喜歡將佈線靠近源,而另外一些開發人員則認爲註釋類再也不是POJO,並且配置變得分散且難以控制。html

不管選擇如何,Spring均可以兼顧兩種風格,甚至能夠將它們混合在一塊兒。值得指出的是,經過其JavaConfig選項,Spring容許以非侵入方式使用註釋,而無需觸及目標組件源代碼,而且在工具方面,Spring Tool Suite支持全部配置樣式 。java

基於註釋的配置提供了XML設置的替代方案,該配置依賴於字節碼元數據來鏈接組件而不是角括號聲明。開發人員不是使用XML來描述bean鏈接,而是經過在相關的類,方法或字段聲明上使用註釋將配置移動到組件類自己。如示例中所述:RequiredAnnotationBeanPostProcessor使用BeanPostProcessor與註釋結合使用是擴展Spring IoC容器的經常使用方法。例如,Spring 2.0引入了使用@Required註釋強制執行所需屬性的可能性。Spring 2.5使得有可能採用相同的通用方法來驅動Spring的依賴注入。基本上,@Autowired註釋提供與自動裝配協做者中描述的相同的功能,但具備更細粒度的控制和更普遍的適用性。Spring 2.5還增長了對JSR-250註釋的支持,例如 @PostConstruct和@PreDestroy。Spring 3.0增長了對javax.inject包中包含的JSR-330(Java的依賴注入)註釋的支持,例如@Inject 和@Named。有關這些註釋的詳細信息,請參閱 相關章節。web

註釋注入在XML注入以前執行。所以,XML配置會覆蓋經過這兩種方法鏈接的屬性的註釋。算法

與往常同樣,您能夠將它們註冊爲單獨的bean定義,但也能夠經過在基於XML的Spring配置中包含如下標記來隱式註冊它們(請注意包含context命名空間):spring

<?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 https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">

    <context:annotation-config/>

</beans>
複製代碼

在隱式註冊後處理器包括 AutowiredAnnotationBeanPostProcessor, CommonAnnotationBeanPostProcessor, PersistenceAnnotationBeanPostProcessor,和前面提到的 RequiredAnnotationBeanPostProcessor。)編程

context:annotation-config/僅查找在定義它的同一應用程序上下文中的bean上的註釋。這意味着,若是你 context:annotation-config/輸入一個WebApplicationContextfor DispatcherServlet,它只會檢查@Autowired你的控制器中的bean,而不是你的服務。有關更多信息,請參閱 DispatcherServlet。api

1.9.1 @Required

該@Required註釋適用於bean屬性setter方法,以下面的例子:數組

public class SimpleMovieLister {緩存

private MovieFinder movieFinder;

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

// ...
}
複製代碼

此批註指示必須在配置時經過bean定義中的顯式屬性值或經過自動裝配填充受影響的bean屬性。若是還沒有填充受影響的bean屬性,則容器將引起異常。這容許急切和明確的失敗,之後避免NullPointerException 實例等。咱們仍然建議您將斷言放入bean類自己(例如,轉換爲init方法)。即便您在容器外部使用類,這樣作也會強制執行那些必需的引用和值。安全

從@RequiredSpring Framework 5.1開始,註釋正式被棄用,支持使用構造函數注入所需的設置(或者InitializingBean.afterPropertiesSet()bean屬性setter方法的自定義實現 )。

在本節包含的示例中,JSR 330的@inject註釋能夠代替spring的@autowired註釋。有關詳細信息,請參閱此處。

能夠將@autowired註釋應用於構造函數,以下例所示:

public class MovieRecommender {

    private final CustomerPreferenceDao customerPreferenceDao;

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

    // ...
}
複製代碼

從Spring Framework 4.3開始,@Autowired若是目標bean只定義了一個開頭的構造函數,則再也不須要對這樣的構造函數進行註釋。可是,若是有幾個構造器可用,則必須註釋至少一個構造器以教導容器使用哪個。

您還能夠將@Autowired註釋應用於「傳統」setter方法,如如下示例所示:

public class SimpleMovieLister {

    private MovieFinder movieFinder;

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

    // ...
}
複製代碼

您還能夠將註釋應用於具備任意名稱和多個參數的方法,如如下示例所示:

public class MovieRecommender {

    private MovieCatalog movieCatalog;

    private CustomerPreferenceDao customerPreferenceDao;

    @Autowired
    public void prepare(MovieCatalog movieCatalog,
            CustomerPreferenceDao customerPreferenceDao) {
        this.movieCatalog = movieCatalog;
        this.customerPreferenceDao = customerPreferenceDao;
    }

    // ...
}

複製代碼

您也能夠應用於@Autowired字段,甚至能夠將它與構造函數混合使用,以下例所示:

public class MovieRecommender {

    private final CustomerPreferenceDao customerPreferenceDao;

    @Autowired
    private MovieCatalog movieCatalog;

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

    // ...
}

複製代碼

您還能夠ApplicationContext 經過將註釋添加到須要該類型數組的字段或方法來提供特定類型的全部bean ,如如下示例所示:

public class MovieRecommender {

    @Autowired
    private MovieCatalog[] movieCatalogs;

    // ...
}

複製代碼

您的目標bean能夠實現org.springframework.core.ordered接口,或者若是您但願數組或列表中的項按特定順序排序,可使用@order或standard@priority註釋。不然,它們的順序遵循容器中相應目標bean定義的註冊順序。 您能夠@Order在目標類級別和@Bean方法上聲明註釋,多是經過單個bean定義(在多個定義使用相同bean類的狀況下)。@Order值可能會影響注入點的優先級,但要注意它們不會影響單例啓動順序,這是由依賴關係和@DependsOn聲明肯定的正交關注點。

請注意,標準javax.annotation.Priority註釋在該@Bean級別不可用 ,由於它沒法在方法上聲明。它的語義能夠經過@Order值與@Primary每種類型的單個bean 相結合來建模。

Map只要預期的密鑰類型是,即便是類型化的實例也能夠自動裝配String。Map值包含全部指望類型的bean,而且鍵包含相應的bean名稱,如如下示例所示:

public class MovieRecommender {

    private Map<String, MovieCatalog> movieCatalogs;

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

    // ...
}
複製代碼

默認狀況下,當沒有匹配的候選bean可用於給定的注入點時,自動佈線將失敗。對於聲明的數組、集合或映射,至少須要一個匹配元素。

默認行爲是將帶註釋的方法和字段視爲指示所需的依賴項。您能夠更改此行爲,如如下示例所示,使框架可以經過將其標記爲非必需來跳過不可知足的注入點:

public class SimpleMovieLister {

    private MovieFinder movieFinder;

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

    // ...
}
複製代碼

若是不可用的依賴項(或多個參數狀況下的依賴項之一),則根本不會調用非必需的方法。在這種狀況下,根本不會填充非必填字段,保留其默認值。

注入的構造函數和工廠方法參數是一種特殊狀況,由於@Autowired因爲Spring的構造函數解析算法可能涉及多個構造函數,所以'required'標誌的含義有些不一樣。默認狀況下有效地須要構造函數和工廠方法參數,但在單構造函數場景中有一些特殊規則,例如,若是沒有匹配的bean可用,則解析爲空實例的多元素注入點(數組,集合,映射)。這容許一個通用的實現模式,其中全部依賴項均可以在一個惟一的多參數構造函數中聲明,例如聲明爲沒有@Autowired註釋的單個公共構造函數。

每一個類只能標記一個帶註釋的構造函數,但能夠註釋多個非必需的構造函數。在這種狀況下,每一個都被認爲是候選者之一,Spring使用最貪婪的構造函數,其依賴性能夠獲得知足 - 也就是說,具備最多參數的構造函數。構造函數解析算法與具備重載構造函數的非註釋類相同,只是將候選者縮小到帶註釋的構造函數。

建議使用「必需」屬性而@Autowired不是@Requiredsetter方法的註釋。「required」屬性表示該屬性不是自動裝配所必需的。若是沒法自動裝配,則會忽略該屬性。@Required另外一方面,它更強大,由於它強制經過容器支持的任何方式設置屬性。若是未定義任何值,則會引起相應的異常。

或者,您能夠經過Java 8表達特定依賴關係的非必需特性java.util.Optional,如如下示例所示:

public class SimpleMovieLister {

    @Autowired
    public void setMovieFinder(Optional<MovieFinder> movieFinder) {
        ...
    }
}
複製代碼

從Spring Framework 5.0開始,您還可使用@Nullable註釋(任何包中的任何類型的註釋 - 例如,javax.annotation.Nullable來自JSR-305):

public class SimpleMovieLister {

    @Autowired
    public void setMovieFinder(@Nullable MovieFinder movieFinder) {
        ...
    }
}
複製代碼

您還可使用@Autowired對於那些衆所周知的解析依賴接口:BeanFactory,ApplicationContext,Environment,ResourceLoader, ApplicationEventPublisher,和MessageSource。這些接口及其擴展接口(如ConfigurableApplicationContext或ResourcePatternResolver)會自動解析,無需特殊設置。如下示例自動裝配一個ApplicationContext對象:

public class MovieRecommender {

    @Autowired
    private ApplicationContext context;

    public MovieRecommender() {
    }

    // ...
}

複製代碼

@Autowired@Inject@Value,和@Resource註釋由Spring處理 BeanPostProcessor實現。這意味着您沒法在本身的類型BeanPostProcessorBeanFactoryPostProcessor類型(若是有)中應用這些註釋。必須使用XML或Spring @Bean方法顯式地「鏈接」這些類型。

1.9.3 微調基於註釋的自動裝配@Primary

因爲按類型自動裝配可能會致使多個候選人,所以一般須要對選擇過程進行更多控制。實現這一目標的一種方法是使用Spring的@Primary註釋。@Primary表示當多個bean能夠自動裝配到單值依賴項時,應該優先選擇特定的bean。若是候選者中只存在一個主bean,則它將成爲自動裝配的值。

請考慮如下定義firstMovieCatalog爲主要的配置MovieCatalog

@Configuration
public class MovieConfiguration {

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

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

    // ...
}
複製代碼

使用上述配置,如下MovieRecommender內容自動裝配 firstMovieCatalog

public class MovieRecommender {

    @Autowired
    private MovieCatalog movieCatalog;

    // ...
}
複製代碼

相應的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 https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://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"/>
複製代碼

1.9.4。使用限定符微調基於註釋的自動裝配

@Primary當能夠肯定一個主要候選者時,是經過具備多個實例的類型使用自動裝配的有效方式。當您須要更多控制選擇過程時,可使用Spring的@Qualifier註釋。您能夠將限定符值與特定參數相關聯,縮小類型匹配集,以便爲每一個參數選擇特定的bean。在最簡單的狀況下,這能夠是一個簡單的描述性值,如如下示例所示:

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

    // ...
}
複製代碼

1.9.4。使用限定符微調基於註釋的自動裝配

@Primary當能夠肯定一個主要候選者時,是經過具備多個實例的類型使用自動裝配的有效方式。當您須要更多控制選擇過程時,可使用Spring的@Qualifier註釋。您能夠將限定符值與特定參數相關聯,縮小類型匹配集,以便爲每一個參數選擇特定的bean。在最簡單的狀況下,這能夠是一個簡單的描述性值,如如下示例所示:

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

    // ...
}
複製代碼

如下示例顯示了相應的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 https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://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>

複製代碼
具備main限定符值的bean與使用相同值限定的構造函數參數鏈接。
具備action限定符值的bean與使用相同值限定的構造函數參數鏈接。

對於回退匹配,bean名稱被視爲默認限定符值。所以,能夠用一個定義bean idmain代替嵌套限定符元素,致使相同的匹配結果。可是,儘管您可使用此約定來按名稱引用特定bean,但@Autowired基本上是關於具備可選語義限定符的類型驅動注入。這意味着即便使用bean名稱回退,限定符值在類型匹配集中也老是具備縮小的語義。它們在語義上不表示對惟一bean的引用id。良好限定的值是mainEMEApersistent,表達獨立於從所述豆的特定部件的特性id,在匿名bean定義的狀況下能夠自動生成,例如前面例子中的定義。

限定符也適用於類型集合,如前所述 - 例如,to Set<MovieCatalog>。在這種狀況下,根據聲明的限定符,全部匹配的bean都做爲集合注入。這意味着限定符沒必要是惟一的。相反,它們構成了過濾標準。例如,您能夠MovieCatalog使用相同的限定符值「action」 定義多個bean,全部這些bean都注入帶Set<MovieCatalog>註釋的註釋中@Qualifier("action")

在類型匹配候選項中,根據目標bean名稱選擇限定符值,不須要@Qualifier注入點處的註釋。若是沒有其餘解析指示符(例如限定符或主要標記),則對於非惟一依賴性狀況,Spring會將注入點名稱(即字段名稱或參數名稱)與目標bean名稱進行匹配,而後選擇同名的候選人,若是有的話。

也就是說,若是您打算按名稱表達註釋驅動的注入,請不要主要使用@Autowired,即便它可以在類型匹配候選項中經過bean名稱進行選擇。相反,使用JSR-250 @Resource註釋,該註釋在語義上定義爲經過其惟一名稱標識特定目標組件,聲明的類型與匹配過程無關。@Autowired具備至關不一樣的語義:在按類型選擇候選bean以後,String 僅在那些類型選擇的候選中考慮指定的限定符值(例如,將account限定符與標記有相同限定符標籤的bean 匹配)。

對於自身定義爲集合Map或數組類型的bean來講,這@Resource 是一個很好的解決方案,它經過惟一名稱引用特定的集合或數組bean。也就是說,從4.3開始,只要在返回類型簽名或集合繼承層次結構中保留元素類型信息,就能夠Map經過Spring的@Autowired類型匹配算法匹配和數組類型 @Bean。在這種狀況下,您可使用限定符值在相同類型的集合中進行選擇,如上一段所述。

從4.3開始,@Autowired還考慮了自引用注入(即,引用回到當前注入的bean)。請注意,自我注入是一種後備。對其餘組件的常規依賴性始終具備優先權。從這個意義上說,自我引用並不參與常規的候選人選擇,所以特別是否是主要的。相反,它們老是最低優先級。在實踐中,您應該僅使用自引用做爲最後的手段(例如,經過bean的事務代理調用同一實例上的其餘方法)。考慮在這種狀況下將受影響的方法分解爲單獨的委託bean。或者,您可使用@Resource,它能夠經過其惟一名稱獲取代理回到當前bean。

@Autowired適用於字段,構造函數和多參數方法,容許在參數級別經過限定符註釋縮小範圍。相比之下,@Resource 僅支持具備單個參數的字段和bean屬性setter方法。所以,若是注射目標是構造函數或多參數方法,則應該使用限定符。

您能夠建立本身的自定義限定符註釋。爲此,請定義註釋並@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;
    }

    // ...
}
複製代碼

接下來,您能夠提供候選bean定義的信息。您能夠將<qualifier/>標記添加爲 標記的子元素,<bean/>而後指定typevalue匹配自定義限定符註釋。類型與註釋的徹底限定類名匹配。或者,爲方便起見,若是不存在衝突名稱的風險,您可使用短類名稱。如下示例演示了這兩種方法:

<?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 https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://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>

複製代碼

類路徑掃描和託管組件中,您能夠看到基於註釋的替代方法,即在XML中提供限定符元數據。具體來講,請參閱使用註釋提供限定符元數據

在某些狀況下,使用沒有值的註釋可能就足夠了。當註釋用於更通用的目的而且能夠應用於多種不一樣類型的依賴項時,這可能頗有用。例如,您能夠提供可在沒有Internet鏈接時搜索的脫機目錄。首先,定義簡單註釋,如如下示例所示:

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

}

複製代碼

而後將註釋添加到要自動裝配的字段或屬性中,如如下示例所示:

public class MovieRecommender {

    @Autowired
    @Offline 
    private MovieCatalog offlineCatalog;

    // ...
}

複製代碼
此行添加@Offline註釋。

如今bean定義只須要一個限定符type,以下例所示:

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

複製代碼
此元素指定限定符。

您還能夠定義除簡單value屬性以外或代替簡單屬性接受命名屬性的自定義限定符註釋。若是隨後在要自動裝配的字段或參數上指定了多個屬性值,則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 https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://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>
複製代碼

1.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>),您能夠@AutowireStore接口和泛型用做限定符,以下例所示:

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

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

複製代碼

1.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>),您能夠@AutowireStore接口和泛型用做限定符,以下例所示:

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

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

複製代碼

通用限定符也適用於自動裝配列表,Map實例和數組。如下示例自動裝配通用List

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

複製代碼

1.9.6。運用CustomAutowireConfigurer

CustomAutowireConfigurer 是一個BeanFactoryPostProcessor容許您註冊本身的自定義限定符註釋類型的,即便它們沒有使用Spring的@Qualifier註釋進行註釋。如下示例顯示如何使用CustomAutowireConfigurer

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

複製代碼

經過如下方式AutowireCandidateResolver肯定autowire候選人:

  • autowire-candidate每一個bean定義的值
  • 元素default-autowire-candidates上可用的任何模式<beans/>
  • @Qualifier註釋的存在以及註冊的任何自定義註釋CustomAutowireConfigurer

當多個bean有資格做爲autowire候選者時,「primary」的肯定以下:若是候選者中只有一個bean定義具備primary 設置爲的屬性true,則選擇它。

1.9.7。注射用@Resource

Spring還經過在字段或bean屬性setter方法上使用JSR-250 @Resourceannotation(javax.annotation.Resource)來支持注入。這是Java EE中的常見模式:例如,在JSF管理的bean和JAX-WS端點中。Spring也支持Spring管理對象的這種模式。

@Resource採用名稱屬性。默認狀況下,Spring將該值解釋爲要注入的bean名稱。換句話說,它遵循按名稱語義,如如下示例所示:

public class SimpleMovieLister {

    private MovieFinder movieFinder;

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

複製代碼

這條線注入了一個@Resource

若是未明確指定名稱,則默認名稱是從字段名稱或setter方法派生的。若是是字段,則採用字段名稱。在setter方法的狀況下,它採用bean屬性名稱。下面的例子將把bean movieFinder注入其setter方法:

public class SimpleMovieLister {

    private MovieFinder movieFinder;

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

複製代碼

提供註解的名稱解析由一個bean的名稱 ApplicationContext,其中的CommonAnnotationBeanPostProcessor知道。若是您SimpleJndiBeanFactory 明確配置Spring,則能夠經過JNDI解析名稱 。可是,咱們建議您依賴於默認行爲並使用Spring的JNDI查找功能來保留間接級別。

在專屬狀況下,@Resource不指定明確的名稱,以及相似的使用@Autowired@Resource發現的主要類型的比賽,而不是一個具體的bean並解決衆所周知的解析依存關係:BeanFactoryApplicationContextResourceLoaderApplicationEventPublisher,和MessageSource 接口。

所以,在如下示例中,customerPreferenceDao字段首先查找名爲「customerPreferenceDao」的bean,而後返回到該類型的主要類型匹配 CustomerPreferenceDao

public class MovieRecommender {

    @Resource
    private CustomerPreferenceDao customerPreferenceDao;

    @Resource
    private ApplicationContext context; 

    public MovieRecommender() {
    }

    // ...
}
複製代碼

context根據已知的可解析依賴類型注入該字段: ApplicationContext

1.9.8。使用@PostConstruct@PreDestroy

CommonAnnotationBeanPostProcessor不只認可了@Resource註解也是JSR-250的生命週期註解:javax.annotation.PostConstructjavax.annotation.PreDestroy。在Spring 2.5中引入,對這些註釋的支持提供了初始化回調銷燬回調中描述的生命週期回調機制的替代 方法。若是 CommonAnnotationBeanPostProcessor在Spring中註冊ApplicationContext,則在生命週期的同一點調用帶有這些註釋之一的方法,做爲相應的Spring生命週期接口方法或顯式聲明的回調方法。在如下示例中,緩存在初始化時預填充並在銷燬時清除:

public class CachingMovieLister {

    @PostConstruct
    public void populateMovieCache() {
        // populates the movie cache upon initialization...
    }

    @PreDestroy
    public void clearMovieCache() {
        // clears the movie cache upon destruction...
    }
}

複製代碼

有關組合各類生命週期機制的效果的詳細信息,請參閱 組合生命週期機制

例如@Resource@PostConstruct@PreDestroy註釋類型是JDK 6到8的標準Java庫的一部分。可是,整個javax.annotation 包與JDK 9中的核心Java模塊分離,最終在JDK 11中刪除。若是須要,javax.annotation-api工件須要是如今經過Maven Central得到,只需像任何其餘庫同樣添加到應用程序的類路徑中。


1.10 1.11再補


1.12 基於Java的容器配置

本節介紹如何在Java代碼中使用註釋來配置Spring容器。它包括如下主題:

1.12.1。基本概念:@Bean@Configuration

Spring的新Java配置支持中的中心工件是 @Configuration註釋類和@Bean註釋方法。

@Bean註釋被用於指示一個方法實例,配置和初始化爲經過Spring IoC容器進行管理的新對象。對於那些熟悉Spring的<beans/>XML配置的人來講,@Bean註釋與<bean/>元素扮演的角色相同。你能夠@Bean在任何Spring中使用-annotated方法 @Component。可是,它們最經常使用於@Configuration bean類。

對類進行註釋@Configuration代表其主要目的是做爲bean定義的來源。此外,@Configuration類容許經過調用@Bean同一類中的其餘方法來定義bean間依賴關係。最簡單的@Configuration類以下:

@Configuration
public class AppConfig {

    @Bean
    public MyService myService() {
        return new MyServiceImpl();
    }
}

複製代碼

上面的AppConfig類等效於如下Spring <beans/>XML:

<beans>
    <bean id="myService" class="com.acme.services.MyServiceImpl"/>
</beans>

複製代碼

完整@Configuration vs「lite」@Bean模式?

@Bean在未註釋的類中聲明方法時 @Configuration,它們被稱爲以「精簡」模式處理。在一個@Component或甚至在一個普通的舊類中聲明的Bean方法被認爲是「精簡」,包含類的主要目的不一樣,而且@Bean方法在那裏是一種獎勵。例如,服務組件能夠經過@Bean每一個適用組件類的附加方法將管理視圖公開給容器。在這種狀況下,@Bean方法是通用的工廠方法機制。

與full不一樣@Configuration,lite @Bean方法不能聲明bean間依賴關係。相反,它們對其包含組件的內部狀態進行操做,而且可選地,對它們能夠聲明的參數進行操做。@Bean所以,這種方法不該該引用其餘 @Bean方法。每一個這樣的方法實際上只是特定bean引用的工廠方法,沒有任何特殊的運行時語義。這裏的積極反作用是沒必要在運行時應用CGLIB子類,所以在類設計方面沒有限制(也就是說,包含類多是final等等)。

在常見的場景中,@Bean方法將在@Configuration類中聲明,確保始終使用「完整」模式,並所以將交叉方法引用重定向到容器的生命週期管理。這能夠防止@Bean經過常規Java調用意外地調用相同的 方法,這有助於減小在「精簡」模式下操做時難以跟蹤的細微錯誤。

@Bean@Configuration註解的深度在如下章節中討論。首先,咱們將介紹使用基於Java的配置建立彈簧容器的各類方法。

@Bean@Configuration註解的深度在如下章節中討論。首先,咱們將介紹使用基於Java的配置建立彈簧容器的各類方法。

1.12.2 使用實例化Spring容器AnnotationConfigApplicationContext

如下部分AnnotationConfigApplicationContext介紹了在Spring 3.0中引入的Spring。這種通用ApplicationContext實現不只可以接受@Configuration類做爲輸入,還能接受 @Component使用JSR-330元數據註釋的普通類和類。

@Configuration提供類做爲輸入時,@Configuration類自己被註冊爲bean定義,而且@Bean類中的全部聲明的方法也被註冊爲bean定義。

@Component提供JSR-330類時,它們被註冊爲bean定義,而且假定DI元數據例如@Autowired@Inject在必要時在這些類中使用。

簡單的施工

與實例化a時Spring XML文件用做輸入的方式大體相同 ClassPathXmlApplicationContext,能夠@Configuration在實例化時使用類做爲輸入AnnotationConfigApplicationContext。這容許徹底無XML使用Spring容器,如如下示例所示:

public static void main(String[] args) {
    ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
    MyService myService = ctx.getBean(MyService.class);
    myService.doStuff();
}

複製代碼

如前所述,AnnotationConfigApplicationContext並不只限於使用@Configuration類。@Component能夠將任何或JSR-330帶註釋的類做爲輸入提供給構造函數,如如下示例所示:

public static void main(String[] args) {
    ApplicationContext ctx = new AnnotationConfigApplicationContext(MyServiceImpl.class, Dependency1.class, Dependency2.class);
    MyService myService = ctx.getBean(MyService.class);
    myService.doStuff();
}

複製代碼

前面的例子中假定MyServiceImplDependency1以及Dependency2使用Spring依賴注入註解,例如@Autowired

用編程方式構建容器 register(Class<?>…​)

您能夠AnnotationConfigApplicationContext使用no-arg構造函數實例化一個,而後使用該register()方法對其進行配置。這種方法在以編程方式構建時特別有用AnnotationConfigApplicationContext。如下示例顯示瞭如何執行此操做:

public static void main(String[] args) {
    AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
    ctx.register(AppConfig.class, OtherConfig.class);
    ctx.register(AdditionalConfig.class);
    ctx.refresh();
    MyService myService = ctx.getBean(MyService.class);
    myService.doStuff();
}
複製代碼
使用啓用組件掃描 scan(String…​)

要啓用組件掃描,您能夠@Configuration按以下方式註釋您的類:

@Configuration
@ComponentScan(basePackages = "com.acme") 
public class AppConfig  {
    ...
}
複製代碼

此註釋可啓用組件掃描。

有經驗的Spring用戶可能熟悉與Spring context:命名空間等效的XML聲明,以下例所示:

<beans>
    <context:component-scan base-package="com.acme"/>
</beans>
複製代碼

在前面的示例中,com.acme掃描包以查找任何已 @Component註釋的類,並將這些類註冊爲容器中的Spring bean定義。AnnotationConfigApplicationContext公開 scan(String…​)方法以容許相同的組件掃描功能,如如下示例所示:

public static void main(String[] args) {
    AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
    ctx.scan("com.acme");
    ctx.refresh();
    MyService myService = ctx.getBean(MyService.class);
}

複製代碼

請記住,@Configuration類是元註釋@Component,所以它們是組件掃描的候選者。在前面的示例中,假設AppConfigcom.acme包(或下面的任何包)中聲明瞭它,它在調用期間被拾取scan()。以後refresh(),它的全部@Bean 方法都被處理並在容器中註冊爲bean定義。

支持Web應用程序 AnnotationConfigWebApplicationContext

可用的WebApplicationContext變體。在配置Spring servlet偵聽器,Spring MVC等時 ,可使用此實現。如下代碼段配置典型的Spring MVC Web應用程序(請注意context-param和init-param的使用):AnnotationConfigApplicationContext``AnnotationConfigWebApplicationContext``ContextLoaderListener``DispatcherServlet``web.xml``contextClass

<!-- Configure ContextLoaderListener to use AnnotationConfigWebApplicationContext
        instead of the default XmlWebApplicationContext -->
    <context-param>
        <param-name>contextClass</param-name>
        <param-value>
            org.springframework.web.context.support.AnnotationConfigWebApplicationContext
        </param-value>
    </context-param>

    <!-- Configuration locations must consist of one or more comma- or space-delimited
        fully-qualified @Configuration classes. Fully-qualified packages may also be
        specified for component-scanning -->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>com.acme.AppConfig</param-value>
    </context-param>

    <!-- Bootstrap the root application context as usual using ContextLoaderListener -->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <!-- Declare a Spring MVC DispatcherServlet as usual -->
    <servlet>
        <servlet-name>dispatcher</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!-- Configure DispatcherServlet to use AnnotationConfigWebApplicationContext
            instead of the default XmlWebApplicationContext -->
        <init-param>
            <param-name>contextClass</param-name>
            <param-value>
                org.springframework.web.context.support.AnnotationConfigWebApplicationContext
            </param-value>
        </init-param>
        <!-- Again, config locations must consist of one or more comma- or space-delimited
            and fully-qualified @Configuration classes -->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>com.acme.web.MvcConfig</param-value>
        </init-param>
    </servlet>

    <!-- map all requests for /app/* to the dispatcher servlet -->
    <servlet-mapping>
        <servlet-name>dispatcher</servlet-name>
        <url-pattern>/app/*</url-pattern>
    </servlet-mapping>
</web-app>

複製代碼

1.12.3。使用@Bean註釋

@Bean是方法級註釋和XML <bean/>元素的直接模擬。註釋支持一些提供的屬性<bean/>,例如:* init-method * destroy-method * autowiring * name

您能夠在帶@Bean註釋的類@Configuration或帶 註釋的類中使用註釋@Component

聲明一個Bean

要聲明bean,可使用註釋註釋方法@Bean。您可使用此方法在ApplicationContext指定爲方法的返回值的類型中註冊bean定義。默認狀況下,bean名稱與方法名稱相同。如下示例顯示了@Bean方法聲明:

@Configuration
public class AppConfig {

    @Bean
    public TransferServiceImpl transferService() {
        return new TransferServiceImpl();
    }
}

複製代碼

上述配置與如下Spring XML徹底等效:

<beans>
    <bean id="transferService" class="com.acme.TransferServiceImpl"/>
</beans>

複製代碼

這兩個聲明都將一個名爲transferServiceavailable 的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 須要a AccountRepository,咱們可使用方法參數來實現該依賴關係,以下例所示:

@Configuration
public class AppConfig {

    @Bean
    public TransferService transferService(AccountRepository accountRepository) {
        return new TransferServiceImpl(accountRepository);
    }
}

複製代碼

解析機制與基於構造函數的依賴注入很是類似。有關詳細信息,請參閱相關部分

接收生命週期回調

使用@Bean註釋定義的任何類都支持常規生命週期回調,而且可使用JSR-250中的註釋@PostConstruct@PreDestroy註釋。有關更多詳細信息,請參閱 JSR-250註釋

徹底支持常規的Spring 生命週期回調。若是bean實現InitializingBeanDisposableBean或者Lifecycle它們各自的方法由容器調用。

還徹底支持標準*Aware接口集(例如BeanFactoryAwareBeanNameAwareMessageSourceAwareApplicationContextAware等)。

@Bean註釋支持指定任意初始化和銷燬回調方法,就像春天XML的init-method,並destroy-method在屬性上的bean元素,以下例所示:

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配置定義的具備public closeshutdown method的bean 會自動使用銷燬回調登記。若是您有公共 closeshutdown方法,而且您不但願在容器關閉時調用它,則能夠添加@Bean(destroyMethod="")到bean定義以禁用默認(inferred)模式。

對於使用JNDI獲取的資源,您可能但願默認執行此操做,由於其生命週期在應用程序以外進行管理。特別是,確保始終爲a執行此操做DataSource,由於已知它在Java EE應用程序服務器上存在問題。

如下示例顯示如何防止自動銷燬回調 DataSource

@Bean(destroyMethod="")
public DataSource dataSource() throws NamingException {
 return (DataSource) jndiTemplate.lookup("MyDS");
}

複製代碼

此外,使用@Bean方法,您一般使用編程JNDI查找,經過使用Spring JndiTemplateJndiLocatorDelegate幫助程序或直接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的範圍。

使用@Scope註釋

您能夠指定使用@Bean註釋定義的bean 應具備特定範圍。您可使用Bean Scopes部分中指定的任何標準做用域 。

默認範圍是singleton,但您可使用@Scope註釋覆蓋它,如如下示例所示:

@Configuration
public class MyConfiguration {

    @Bean
    @Scope("prototype")
    public Encryptor encryptor() {
        // ...
    }
}

複製代碼
@Scopescoped-proxy

Spring提供了一種經過做用域代理處理做用域依賴項的便捷方法 。使用XML配置時建立此類代理的最簡單方法是<aop:scoped-proxy/>元素。使用@Scope註釋在Java中配置bean 提供了對該proxyMode屬性的等效支持。默認值爲no proxy(ScopedProxyMode.NO),但您能夠指定ScopedProxyMode.TARGET_CLASSScopedProxyMode.INTERFACES

若是將scoped代理示例從XML參考文檔(請參閱範圍代理)移植 到@Bean使用Java,它相似於如下內容:

// 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 Aliasing 別名

正如Naming Beans中所討論的,有時須要爲單個bean提供多個名稱,也稱爲bean別名。 爲此目的name@Bean註釋的屬性接受String數組。如下示例顯示如何爲bean設置多個別名:

@Configuration
public class AppConfig {

    @Bean({"dataSource", "subsystemA-dataSource", "subsystemB-dataSource"})
    public DataSource dataSource() {
        // instantiate, configure and return DataSource bean...
    }
}

複製代碼
Bean描述

有時,提供更詳細的bean文本描述會頗有幫助。當bean(可能經過JMX)進行監視時,這可能特別有用。

要向a添加描述@Bean,可使用 @Description 註釋,如如下示例所示:

@Configuration
public class AppConfig {

    @Bean
    @Description("Provides a basic example of a bean")
    public Thing thing() {
        return new Thing();
    }
}

複製代碼

1.12.4。使用@Configuration註釋

@Configuration是一個類級別的註釋,指示對象是bean定義的來源。@Configurationclasses經過公共@Bean註釋方法聲明bean 。@Bean@Configuration類上的方法的調用也可用於定義bean間依賴項。請參閱基本概念:@Bean@Configuration通常性介紹。

注入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構造函數注入的引用。

這種聲明bean間依賴關係的@Bean方法只有在@Configuration類中聲明方法時纔有效。您不能使用普通@Component類聲明bean間依賴項。

這種聲明bean間依賴關係的@Bean方法只有在@Configuration類中聲明方法時纔有效。您不能使用普通@Component類聲明bean間依賴項。

查找方法注入

如前所述,查找方法注入是一項不多使用的高級功能。在單例範圍的bean依賴於原型範圍的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其中抽象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();
    }
}

複製代碼

clientDao()被稱爲一次進入clientService1()和進入一次clientService2()。因爲此方法建立了一個新實例ClientDaoImpl並將其返回,所以一般須要兩個實例(每一個服務一個)。這確定會有問題:在Spring中,實例化的bean singleton默認具備範圍。這就是魔術的用武之地:全部@Configuration類都在啓動時被子類化CGLIB。在子類中,子方法在調用父方法並建立新實例以前,首先檢查容器是否有任何緩存(做用域)bean。

根據bean的範圍,行爲可能會有所不一樣。咱們在這裏談論單身人士。

從Spring 3.2開始,再也不須要將CGLIB添加到類路徑中,由於CGLIB類已經從新打包org.springframework.cglib並直接包含在spring-core JAR中。

因爲CGLIB在啓動時動態添加功能,所以存在一些限制。特別是,配置類不能是最終的。可是,從4.3開始,配置類容許使用任何構造函數,包括使用 @Autowired默認注入的單個非默認構造函數聲明。

若是您但願避免任何CGLIB強加的限制,請考慮@Bean 在非@Configuration類上聲明您的方法(例如,在普通@Component類上)。@Bean而後攔截方法之間的跨方法調用,所以您必須徹底依賴於構造函數或方法級別的依賴注入。

1.12.5。編寫基於Java的配置

@Configuration
public class ConfigA {

    @Bean
    public A a() {
        return new A();
    }
}

@Configuration
@Import(ConfigA.class)
public class ConfigB {

    @Bean
    public B b() {
        return new B();
    }
}

複製代碼

如今,不須要同時指定ConfigA.classConfigB.class實例化上下文,只ConfigB須要顯式提供,以下例所示:

public static void main(String[] args) {
    ApplicationContext ctx = new AnnotationConfigApplicationContext(ConfigB.class);

    // now both beans A and B will be available...
    A a = ctx.getBean(A.class);
    B b = ctx.getBean(B.class);
}

複製代碼

這種方法簡化了容器實例化,由於只須要處理一個類,而不是要求您@Configuration在構造期間記住可能大量的 類。

從Spring Framework 4.2開始,@Import還支持引用常規組件類,相似於AnnotationConfigApplicationContext.register方法。若是要避免組件掃描,這一點特別有用,可使用一些配置類做爲明肯定義全部組件的入口點。

注入對導入@Bean定義的依賴性

前面的例子有效,但很簡單。在大多數實際狀況中,bean跨配置類彼此依賴。使用XML時,這不是問題,由於不涉及編譯器,而且您能夠聲明 ref="someBean"並信任Spring在容器初始化期間解決它。使用@Configuration類時,Java編譯器會對配置模型施加約束,由於對其餘bean的引用必須是有效的Java語法。

幸運的是,解決這個問題很簡單。正如咱們已經討論過的,一個@Bean方法能夠有任意數量的參數來描述bean的依賴關係。考慮如下更多真實場景​​,其中包含幾個@Configuration 類,每一個類都依賴於其餘類中聲明的bean:

@Configuration
public class ServiceConfig {

    @Bean
    public TransferService transferService(AccountRepository accountRepository) {
        return new TransferServiceImpl(accountRepository);
    }
}

@Configuration
public class RepositoryConfig {

    @Bean
    public AccountRepository accountRepository(DataSource dataSource) {
        return new JdbcAccountRepository(dataSource);
    }
}

@Configuration
@Import({ServiceConfig.class, RepositoryConfig.class})
public class SystemTestConfig {

    @Bean
    public DataSource dataSource() {
        // return new DataSource
    }
}

public static void main(String[] args) {
    ApplicationContext ctx = new AnnotationConfigApplicationContext(SystemTestConfig.class);
    // everything wires up across configuration classes...
    TransferService transferService = ctx.getBean(TransferService.class);
    transferService.transfer(100.00, "A123", "C456");
}
複製代碼

還有另外一種方法能夠達到相同的效果。請記住,@Configuration類最終只是容器中的另外一個bean:這意味着它們能夠利用 @Autowired@Value注入以及與任何其餘bean相同的其餘功能。

確保以這種方式注入的依賴項只是最簡單的類型。@Configuration 在上下文初始化期間很早就處理了類,而且強制以這種方式注入依賴項可能會致使意外的早期初始化。儘量採用基於參數的注入,如前面的示例所示。

另外,要特別注意BeanPostProcessorBeanFactoryPostProcessor定義@Bean。這些一般應該聲明爲static @Bean方法,而不是觸發其包含配置類的實例化。不然,@Autowired@Value不要在配置類自己的工做,由於它是被做爲一個bean實例建立爲時尚

如下示例顯示瞭如何將一個bean自動鏈接到另外一個bean:

@Configuration
public class ServiceConfig {

    @Autowired
    private AccountRepository accountRepository;

    @Bean
    public TransferService transferService() {
        return new TransferServiceImpl(accountRepository);
    }
}

@Configuration
public class RepositoryConfig {

    private final DataSource dataSource;

    @Autowired
    public RepositoryConfig(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    @Bean
    public AccountRepository accountRepository() {
        return new JdbcAccountRepository(dataSource);
    }
}

@Configuration
@Import({ServiceConfig.class, RepositoryConfig.class})
public class SystemTestConfig {

    @Bean
    public DataSource dataSource() {
        // return new DataSource
    }
}

public static void main(String[] args) {
    ApplicationContext ctx = new AnnotationConfigApplicationContext(SystemTestConfig.class);
    // everything wires up across configuration classes...
    TransferService transferService = ctx.getBean(TransferService.class);
    transferService.transfer(100.00, "A123", "C456");
}

複製代碼

@Configuration僅在Spring Framework 4.3中支持類中的 構造函數注入。另請注意,無需指定@Autowired目標bean是否僅定義了一個構造函數。在前面的示例中,構造函數@Autowired上沒有必要RepositoryConfig

徹底符合條件的進口豆類,便於導航

在前面的場景中,使用@Autowired效果很好並提供了所需的模塊性,但肯定聲明自動裝配的bean定義的確切位置仍然有些模棱兩可。例如,做爲開發人員ServiceConfig,您如何確切地知道@Autowired AccountRepositorybean的聲明位置?它在代碼中並不明確,這可能就行了。請記住, Spring Tool Suite提供的工具能夠呈現圖形,顯示全部內容的連線方式,這可能就是您所須要的。此外,您的Java IDE能夠輕鬆找到該AccountRepository類型的全部聲明和用法,並快速顯示@Bean返回該類型的方法的位置。

若是這種歧義是不可接受的,而且您但願從IDE中直接從一個@Configuration類導航到另外一個類,請考慮自行裝配配置類自己。如下示例顯示瞭如何執行此操做:

@Configuration
public class ServiceConfig {

    @Autowired
    private RepositoryConfig repositoryConfig;

    @Bean
    public TransferService transferService() {
        // navigate 'through' the config class to the @Bean method!
        return new TransferServiceImpl(repositoryConfig.accountRepository());
    }
}
複製代碼

在前面的狀況中,AccountRepository定義的位置是徹底明確的。可是,ServiceConfig如今牢牢聯繫在一塊兒RepositoryConfig。這是權衡。經過使用基於接口的或基於@Configuration類的抽象類,能夠在某種程度上減輕這種緊密耦合。請考慮如下示例:

@Configuration
public class ServiceConfig {

    @Autowired
    private RepositoryConfig repositoryConfig;

    @Bean
    public TransferService transferService() {
        return new TransferServiceImpl(repositoryConfig.accountRepository());
    }
}

@Configuration
public interface RepositoryConfig {

    @Bean
    AccountRepository accountRepository();
}

@Configuration
public class DefaultRepositoryConfig implements RepositoryConfig {

    @Bean
    public AccountRepository accountRepository() {
        return new JdbcAccountRepository(...);
    }
}

@Configuration
@Import({ServiceConfig.class, DefaultRepositoryConfig.class})  // import the concrete config!
public class SystemTestConfig {

    @Bean
    public DataSource dataSource() {
        // return DataSource
    }

}

public static void main(String[] args) {
    ApplicationContext ctx = new AnnotationConfigApplicationContext(SystemTestConfig.class);
    TransferService transferService = ctx.getBean(TransferService.class);
    transferService.transfer(100.00, "A123", "C456");
}
複製代碼

如今ServiceConfig與具體的鬆散耦合 DefaultRepositoryConfig,內置的IDE工具仍然有用:您能夠輕鬆得到實現的類型層次結構RepositoryConfig。經過這種方式,導航@Configuration類及其依賴關係與導航基於接口的代碼的常規過程沒有什麼不一樣。

若是要影響某些bean的啓動建立順序,能夠考慮將它們中的一些聲明爲@Lazy(用於在第一次訪問時建立而不是在啓動時)或@DependsOn某些其餘bean(確保在當前bean以前建立特定的其餘bean,超出後者的直接依賴意味着什麼)。

有條件地包括@Configuration類或@Bean方法

基於某些任意系統狀態,有條件地啓用或禁用完整@Configuration類或甚至單個@Bean方法一般頗有用。一個常見的例子是@Profile只有在Spring中啓用了特定的配置文件時才使用註釋激活bean Environment( 有關詳細信息,請參閱Bean定義配置文件)。

@Profile註釋是經過使用一種稱爲更靈活的註釋實際執行@Conditional。該@Conditional註釋指示特定org.springframework.context.annotation.Condition前應諮詢的實施@Bean是註冊。

Condition接口的實現提供了一個matches(…​) 返回true或的方法false。例如,如下清單顯示了Condition用於的實際 實現@Profile

@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
    if (context.getEnvironment() != null) {
        // Read the @Profile annotation attributes
        MultiValueMap<String, Object> attrs = metadata.getAllAnnotationAttributes(Profile.class.getName());
        if (attrs != null) {
            for (Object value : attrs.get("value")) {
                if (context.getEnvironment().acceptsProfiles(((String[]) value))) {
                    return true;
                }
            }
            return false;
        }
    }
    return true;
}
複製代碼

有關@Conditional 更多詳細信息,請參閱javadoc。

結合Java和XML配置

Spring的@Configuration類支持並不是旨在成爲Spring XML的100%徹底替代品。某些工具(如Spring XML命名空間)仍然是配置容器的理想方法。在XML方便或必要的狀況下,您能夠選擇:例如,經過使用「以XML爲中心」的方式實例化容器ClassPathXmlApplicationContext,或者經過使用AnnotationConfigApplicationContext@ImportResource註釋以「以Java爲中心」的方式實例化它。 根據須要導入XML。

以XML爲中心的@Configuration類的使用

最好從XML引導Spring容器並@Configuration以ad-hoc方式包含 類。例如,在使用Spring XML的大型現有代碼庫中,能夠@Configuration根據須要更輕鬆地建立類,並將其包含在現有XML文件中。在本節的後面部分,咱們將介紹@Configuration在這種「以XML爲中心」的狀況下使用類的選項。

@Configuration類聲明爲普通的Spring <bean/>元素

請記住,@Configuration類最終是容器中的bean定義。在本系列示例中,咱們建立了一個@Configuration名爲的類,AppConfig並將其system-test-config.xml做爲<bean/>定義包含在其中。由於 <context:annotation-config/>已打開,容器會識別@Configuration註釋並 正確處理@Bean聲明的方法AppConfig

如下示例顯示了Java中的普通配置類:

@Configuration
public class AppConfig {

    @Autowired
    private DataSource dataSource;

    @Bean
    public AccountRepository accountRepository() {
        return new JdbcAccountRepository(dataSource);
    }

    @Bean
    public TransferService transferService() {
        return new TransferService(accountRepository());
    }
}
複製代碼

如下示例顯示了示例system-test-config.xml文件的一部分:

<beans>
    <!-- enable processing of annotations such as @Autowired and @Configuration -->
    <context:annotation-config/>
    <context:property-placeholder location="classpath:/com/acme/jdbc.properties"/>

    <bean class="com.acme.AppConfig"/>

    <bean class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>
</beans>
複製代碼

如下示例顯示了一個可能的jdbc.properties文件:

jdbc.url = JDBC:HSQLDB:HSQL://本地主機/ XDB jdbc.username = SA jdbc.password =

public static void main(String[] args) {
    ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:/com/acme/system-test-config.xml");
    TransferService transferService = ctx.getBean(TransferService.class);
    // ...
}

複製代碼
system-test-config.xml文件中,AppConfig <bean/>不聲明id 元素。雖然這樣作是能夠接受的,可是沒有必要,由於沒有其餘bean引用它,而且不太可能經過名稱從容器中明確地獲取它。相似地,DataSourcebean只是按類型自動裝配,所以id 不嚴格要求顯式bean 。

使用<context:component-scan />來獲取@Configuration

由於@Configuration帶有元註釋@Component,註釋@Configuration類自動成爲組件掃描的候選者。使用與前一個示例中描述的相同的方案,咱們能夠從新定義system-test-config.xml以利用組件掃描。請注意,在這種狀況下,咱們不須要顯式聲明<context:annotation-config/>,由於<context:component-scan/>啓用相同的功能。

如下示例顯示了已修改的system-test-config.xml文件:

<beans>
    <!-- picks up and registers AppConfig as a bean definition -->
    <context:component-scan base-package="com.acme"/>
    <context:property-placeholder location="classpath:/com/acme/jdbc.properties"/>

    <bean class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>
</beans>

複製代碼
@Configuration 以類爲中心的XML使用 @ImportResource

@Configuration類是配置容器的主要機制的應用程序中,仍然可能須要使用至少一些XML。在這些場景中,您能夠@ImportResource根據須要使用和定義儘量多的XML。這樣作能夠實現「以Java爲中心」的方法來配置容器並將XML保持在最低限度。如下示例(包括配置類,定義bean的XML文件,屬性文件和main類)顯示瞭如何使用@ImportResource註釋來實現根據須要使用XML的「以Java爲中心」的配置:

@Configuration
@ImportResource("classpath:/com/acme/properties-config.xml")
public class AppConfig {

    @Value("${jdbc.url}")
    private String url;

    @Value("${jdbc.username}")
    private String username;

    @Value("${jdbc.password}")
    private String password;

    @Bean
    public DataSource dataSource() {
        return new DriverManagerDataSource(url, username, password);
    }
}
複製代碼
properties-config.xml
<beans>
    <context:property-placeholder location="classpath:/com/acme/jdbc.properties"/>
</beans>
複製代碼

jdbc.properties jdbc.url = JDBC:HSQLDB:HSQL://本地主機/ XDB jdbc.username = SA jdbc.password =

public static void main(String[] args) {
    ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
    TransferService transferService = ctx.getBean(TransferService.class);
    // ...
}
複製代碼
相關文章
相關標籤/搜索