一起來讀官方文件-----SpringIOC(04)

大白鵝養殖基地發表於2020-09-14
1.4.2。依賴性和詳細配置

如上一節所述,您可以將bean屬性和建構函式引數定義為對其他託管bean(協作者)的引用或內聯定義的值。Spring的基於XML的配置後設資料為此目的在其元素中支援子元素型別。

直值(原語,字串等)
在value所述的屬性元素指定屬性或構造器引數的人類可讀的字串表示。Spring的 轉換服務用於將這些值從轉換String為屬性或引數的實際型別。以下示例顯示了設定的各種值:

<bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
    <!-- results in a setDriverClassName(String) call -->
    <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
    <property name="url" value="jdbc:mysql://localhost:3306/mydb"/>
    <property name="username" value="root"/>
    <property name="password" value="misterkaoli"/>
</bean>

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:p="http://www.springframework.org/schema/p"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource"
        destroy-method="close"
        p:driverClassName="com.mysql.jdbc.Driver"
        p:url="jdbc:mysql://localhost:3306/mydb"
        p:username="root"
        p:password="misterkaoli"/>

</beans>

前面的XML更簡潔。但是,拼寫錯誤是在執行時而不是設計時發現的,除非您使用IDE(例如IntelliJ IDEA或用於Eclipse的Spring工具)在建立bean定義時支援自動屬性完成。

您還可以配置java.util.Properties例項,如下所示:

<bean id="mappings"
    class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer">
    <property name="properties">
        <value>
            jdbc.driver.className=com.mysql.jdbc.Driver
            jdbc.url=jdbc:mysql://localhost:3306/mydb
        </value>
    </property>
</bean>

Spring容器將元素中的文字轉換為java.util。屬性例項,通過使用JavaBeans的PropertyEditor機制。這是一個很好的快捷方式,也是Spring團隊偏愛使用巢狀的元素而不是value屬性樣式的幾個地方之一。

idref元素

所述idref元件是一個防錯方法,主要通過將該容器中的另一個bean的id(將id作為一個字串值傳遞-而不是引用)傳遞給標籤。

以下示例顯示瞭如何使用它:

<bean id="theTargetBean" class="..."/>

<bean id="theClientBean" class="...">
    <property name="targetName">
        //此處將 theTargetBean 作為字串傳遞給 theClientBean的targetName屬性
        //而不是將theTargetName這個bean的例項傳遞給targetName屬性
        <idref bean="theTargetBean"/>
    </property>
</bean>

前面的bean定義程式碼段(在執行時)與以下程式碼段完全等效:
idref 等價的是 value標籤 而不是 ref標籤

<bean id="theTargetBean" class="..." />
<bean id="client" class="...">
    <property name="targetName" value="theTargetBean"/>
</bean>
元素 的local屬性在idref4.0 Bean XSD中不再受支援,因為它不再提供常規bean引用上的值。
升級到4.0模式時,將現有idref local引用更改為idref bean。

idref用法 可以校驗傳入的作為bean的id 會被用來校驗當前id的bean存不存在

<idref></idref>元素常用的位置(至少在spring2.0之前的版本中)是在ProxyFactoryBean bean定義中的AOP攔截器配置中。
在指定攔截器名稱時使用<idref></idref>元素可以防止拼寫錯誤。
ref 對其他Bean的引用

ref元素是或定義元素中的最後一個元素。在這裡,您將bean的指定屬性的值設定為對容器管理的另一個bean的引用。被引用的bean是要設定其屬性的bean的依賴項,並且在設定屬性之前根據需要初始化它(如果協作者是單例bean,它可能已經被容器初始化了)。所有引用最終都是對另一個物件的引用。範圍和驗證取決於是否通過bean或父屬性指定其他物件的ID或名稱。

通過標記的bean屬性指定目標bean是最通用的形式,它允許建立對同一容器或父容器中任何bean的引用,而不管它是否在同一XML檔案中。bean屬性的值可以與目標bean的id屬性相同,或者與目標bean的name屬性中的一個值相同。下面的例子展示瞭如何使用ref元素:

//someBean 可以是bean的id  也可以是bean的name
<ref bean="someBean"/>

ref元素的local屬性在ref4.0 Bean XSD中不再受支援,
因為它不再提供常規bean引用上的值。升級到4.0模式時,將現有ref local引用更改ref bean為。

集合

、元素分別設定Java集合型別list、set、map、properties的屬性和引數。下面的例子展示瞭如何使用它們:

<bean id="moreComplexObject" class="example.ComplexObject">
    <!-- results in a setAdminEmails(java.util.Properties) call -->
    <property name="adminEmails">
        <props>
            <prop key="administrator">administrator@example.org</prop>
            <prop key="support">support@example.org</prop>
            <prop key="development">development@example.org</prop>
        </props>
    </property>
    <!-- results in a setSomeList(java.util.List) call -->
    <property name="someList">
        <list>
            <value>a list element followed by a reference</value>
            <ref bean="myDataSource" />
        </list>
    </property>
    <!-- results in a setSomeMap(java.util.Map) call -->
    <property name="someMap">
        <map>
            <entry key="an entry" value="just some string"/>
            <entry key ="a ref" value-ref="myDataSource"/>
        </map>
    </property>
    <!-- results in a setSomeSet(java.util.Set) call -->
    <property name="someSet">
        <set>
            <value>just some string</value>
            <ref bean="myDataSource" />
        </set>
    </property>
</bean>

map的key或value,或set的value 也可以是以下任意元素:

bean | ref | idref | list | set | map | props | value | null
集合合併

Spring容器還支援合併集合。應用開發者可以定義一個父元素或,讓子元素或繼承和覆蓋父元素集合的值。也就是說,子集合的值是合併父集合和子集合的元素的結果,子集合元素覆蓋父集合中指定的值。

關於合併的這一節討論父-子bean機制。不熟悉父bean和子bean定義的讀者可能希望在繼續之前閱讀相關部分。

下面的例子演示了集合合併:

<beans>
    <bean id="parent" abstract="true" class="example.ComplexObject">
        <property name="adminEmails">
            <props>
                <prop key="administrator">administrator@example.com</prop>
                <prop key="support">support@example.com</prop>
            </props>
        </property>
    </bean>
    <bean id="child" parent="parent">
        <property name="adminEmails">
            <!-- the merge is specified on the child collection definition -->
            <props merge="true">
                <prop key="sales">sales@example.com</prop>
                <prop key="support">support@example.co.uk</prop>
            </props>
        </property>
    </bean>
<beans>

注意在子bean定義的adminEmails屬性的元素上使用了merge=true屬性。當容器解析並例項化子bean時,生成的例項具有一個adminEmails屬性集合,該集合包含將子bean的adminEmails集合與父bean的adminEmails集合合併的結果。下面的清單顯示了結果:

administrator=administrator@example.com
sales=sales@example.com
support=support@example.co.uk

子屬性集合的值集繼承父屬性中的所有屬性元素,子屬性支援值的值覆蓋父屬性集合中的值。

這種合併行為類似地應用於集合型別。在元素的特定情況下,將維護與列表集合型別(即值的有序集合的概念)相關聯的語義。父元素的值位於所有子元素列表的值之前。對於Map、Set和Properties集合型別,不存在排序。因此,對於位於容器內部使用的關聯對映、集合和屬性實現型別下的集合型別,排序語義不起作用。

集合合併的侷限性

您不能合併不同的集合型別(例如Map和List)。如果嘗試這樣做,將會丟擲異常。
該merge屬性必須在下面的繼承的子集合定義中指定。
merge在父集合定義上指定屬性是多餘的,不會導致所需的合併。

強型別集合

隨著Java 5中泛型型別的引入,您可以使用強型別集合。也就是說,可以宣告一個Collection型別,使其僅包含(例如)String元素。如果使用Spring將強型別依賴注入Collection到Bean中,則可以利用Spring的型別轉換支援,以便在將強型別Collection 例項的元素新增到之前將其轉換為適當的型別Collection。以下Java類和bean定義顯示瞭如何執行此操作:

public class SomeClass {
    private Map<String, Float> accounts;
    public void setAccounts(Map<String, Float> accounts) {
        this.accounts = accounts;
    }
}
<beans>
    <bean id="something" class="x.y.SomeClass">
        <property name="accounts">
            <map>
                <entry key="one" value="9.99"/>
                <entry key="two" value="2.75"/>
                <entry key="six" value="3.99"/>
            </map>
        </property>
    </bean>
</beans>

當準備註入bean 的accounts屬性時,
可以通過反射獲得something有關強型別的元素型別的泛型資訊Map<String,Float>。
因此,Spring的型別轉換基礎結構將各種值元素識別為type Float,
並將字串值(9.99, 2.75和 3.99)轉換為實際Float型別。
空字串值和空字串值

Spring將屬性之類的空引數視為空字串。以下基於xml的配置後設資料片段將email屬性設定為空字串值("")。

<bean class="ExampleBean">
    <property name="email" value=""/>
</bean>

前面的示例等效於以下Java程式碼:

exampleBean.setEmail("");

元素處理null的值。以下清單顯示了一個示例:

<bean class="ExampleBean">
    <property name="email">
        <null/>
    </property>
</bean>

前面的配置等效於下面的Java程式碼:

exampleBean.setEmail(null);
帶有p-名稱空間的XML快捷方式

p-名稱空間允許您使用bean元素的屬性(而不是巢狀的元素)來描述與之合作的bean的屬性值,或者兩者都使用。

Spring支援帶有名稱空間的可擴充套件配置格式,名稱空間基於XML模式定義。本章中討論的bean配置格式是在XML模式文件中定義的。但是,p-名稱空間沒有在XSD檔案中定義,只存在於Spring的核心中。

下面的示例顯示了兩個解析為相同結果的XML片段(第一個使用標準XML格式,第二個使用p-名稱空間):

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:p="http://www.springframework.org/schema/p" <!-- 這一行是要的 -->
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean name="classic" class="com.example.ExampleBean">
        <property name="email" value="someone@somewhere.com"/>
    </bean>

    <bean name="p-namespace" class="com.example.ExampleBean"
        p:email="someone@somewhere.com"/>
</beans>

該示例顯示了email在bean定義中呼叫的p-namespace中的屬性。這告訴Spring包含一個屬性宣告。如前所述,p名稱空間沒有架構定義,因此可以將屬性名稱設定為屬性名稱。

下一個示例包括另外兩個bean定義,它們都引用了另一個bean:

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:p="http://www.springframework.org/schema/p" 
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean name="john-classic" class="com.example.Person">
        <property name="name" value="John Doe"/>
        <property name="spouse" ref="jane"/>
    </bean>

    <bean name="john-modern"
        class="com.example.Person"
        p:name="John Doe"
        p:spouse-ref="jane"/>

    <bean name="jane" class="com.example.Person">
        <property name="name" value="Jane Doe"/>
    </bean>
</beans>

這個示例不僅包含一個使用p-名稱空間的屬性值,而且還使用一種特殊格式來宣告屬性引用。第一個bean定義使用來建立一個從bean john到bean jane的引用,第二個bean定義使用p:spouse-ref="jane"作為一個屬性來完成完全相同的工作。在本例中,spouse是屬性名,而-ref部分表明這不是一個直接的值,而是對另一個bean的引用。

    p-名稱空間不如標準XML格式靈活。
    例如,宣告屬性引用的格式與以Ref結尾的屬性衝突,而標準XML格式不會。
    我們建議您仔細選擇您的方法,並與您的團隊成員溝通,
    以避免同時生成使用所有三種方法的XML文件。

	The p-namespace is not as flexible as the standard XML format. 
	For example, the format for declaring property references clashes 
	with properties that end in Ref,whereas the standard XML format does not.
	We recommend that you choose your approach carefully 
	and communicate this to your team members to avoid producing XML documents
	that use all three approaches at the same time.
	
	這段沒看懂,這裡測試了以Ref結尾的屬性也是可以用 p:xxxRef-ref
具有c-namespace的XML快捷方式

與使用p-namespace的XML快捷方式類似,Spring 3.1中引入的c-namespace允許使用內聯屬性配置建構函式引數,而不是巢狀建構函式引數元素。說白了就是p-namespace替換property,c-namespace替換constructor-arg

下面的示例使用c:名稱空間執行與 基於建構函式的依賴注入相同的操作:

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:c="http://www.springframework.org/schema/c" <!-- 新增該條 -->
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="beanTwo" class="x.y.ThingTwo"/>
    <bean id="beanThree" class="x.y.ThingThree"/>

    <!-- 帶有可選引數名稱的傳統宣告 -->
    <bean id="beanOne" class="x.y.ThingOne">
        <constructor-arg name="thingTwo" ref="beanTwo"/>
        <constructor-arg name="thingThree" ref="beanThree"/>
        <constructor-arg name="email" value="something@somewhere.com"/>
    </bean>

    <!-- 帶有引數名稱的c-名稱空間宣告 -->
    <bean id="beanOne" class="x.y.ThingOne" c:thingTwo-ref="beanTwo"
        c:thingThree-ref="beanThree" c:email="something@somewhere.com"/>

</beans>

對於建構函式引數名不可用的罕見情況(通常是在編譯位元組碼時沒有除錯資訊的情況下),可以使用回退到引數索引,如下所示:

<bean id="beanOne" class="x.y.ThingOne" c:_0-ref="beanTwo" c:_1-ref="beanThree"
    c:_2="something@somewhere.com"/>
複合屬性名稱 一個bean中巢狀另外一個bean 並需要給內部的bean賦值

在設定bean屬性時,可以使用複合或巢狀屬性名,只要路徑的所有元件(最終屬性名除外)都不為空。考慮下面的bean定義:

<bean id="something" class="things.ThingOne">
    <property name="fred.bob.sammy" value="123" />
</bean>


所述something bean具有fred屬性,該屬性具有bob屬性,
bob屬性具有sammy 特性,並且最終sammy屬性被設定為值123。  
something bean的fred屬性和fred的bob屬性在構造bean之後一定不能為null。  
否則,將會引發NullPointerException。
1.4.3。使用depends-on

如果一個bean是另一個bean的依賴項,則通常意味著將一個bean設定為另一個bean的屬性。通常,您可以使用基於XML的配置後設資料中的 元素來完成此操作。
但是,有時bean之間的依賴性不太直接。一個示例是何時需要觸發類中的靜態初始值設定項,例如用於資料庫驅動程式註冊。該depends-on屬性可以在初始化使用此元素的bean之前顯式強制初始化一個或多個bean。以下示例使用該depends-on屬性表示對單個bean的依賴關係:

<bean id="beanOne" class="ExampleBean" depends-on="manager"/>
<bean id="manager" class="ManagerBean" />

要表達對多個bean的依賴性,就需要用逗號隔開多個名稱

<bean id="beanOne" class="ExampleBean" depends-on="manager,accountDao">
    <property name="manager" ref="manager" />
</bean>

<bean id="manager" class="ManagerBean" />
<bean id="accountDao" class="x.y.jdbc.JdbcAccountDao" />
depends-on屬既可以指定初始化時間依賴項,
也可以指定對應的銷燬時間依賴項(僅在單例bean中)。被依賴的bean會晚於依賴bean之後銷燬

<bean id="serviceOneRef" name="serviceOneName" class="org.springframework.example.service.ServiceOne"
		  destroy-method="destroyed"/>
    public void destroyed(){
		System.out.println("ServiceOne destroy");
	}
	
<bean id="serviceTwo" class="org.springframework.example.service.ServiceTwo"
		  p:name="asdfasdf"  depends-on="serviceOneRef"
		  destroy-method="destroyed" />
    public void destroyed(){
		System.out.println("serviceTwo destroy");
	}
		  
console:
    serviceTwo destroy
    ServiceOne destroy
1.4.4。懶載入bean

預設情況下,作為初始化過程的一部分,ApplicationContext實現會急切地建立和配置所有的單例bean。通常,這種預例項化是可取的,因為配置或周圍環境中的錯誤是立即發現的,而不是幾小時甚至幾天後發現的。
當這種行為不合適時,您可以通過將bean定義標記為延遲初始化來防止單例bean的預例項化。延遲初始化的bean告訴IoC容器在第一次請求bean例項時(而不是在啟動時)建立bean例項。

在XML中,這種行為是由元素的lazy-init屬性控制的,如下面的例子所示:

<bean id="lazy" class="com.something.ExpensiveToCreateBean" lazy-init="true"/>
<bean name="not.lazy" class="com.something.AnotherBean"/>

當ApplicationContext使用前面的配置時,
lazy bean不會在ApplicationContext啟動時急切地預例項化,
not.lazy bean則會被急切地預例項化。

然而,當懶載入的bean是非懶載入的單例bean的依賴項時,ApplicationContext在啟動時則會建立懶載入的bean,因為它必須滿足單例的依賴項。

您還可以通過使用元素的default-lazy-init批量設定懶載入bean

<beans default-lazy-init="true">
    <!-- no beans will be pre-instantiated... -->
</beans>
1.4.5。自動裝配

Spring容器可以自動裝配協作bean之間的關係。
自動裝配具有以下優點:

  • 自動裝配可以大大減少指定屬性或建構函式引數的需要。
  • 隨著物件的發展,自動裝配可以更新配置。例如,如果您需要向類中新增一個依賴項,則無需修改配置即可自動滿足該依賴項。因此,自動裝配在開發過程中特別有用,而不必擔心當程式碼庫變得更穩定時切換到顯式接線的選擇。

使用基於XML的配置後設資料時,可以使用元素的autowire屬性為 bean定義指定自動裝配模式。自動裝配功能具有四種模式。您可以為每個bean指定自動裝配,因此可以選擇要自動裝配的裝配。下表描述了四種自動裝配模式:

模式 解釋
no (預設)沒有自動裝配。想要引用其他的Bean必須由ref元素定義。
對於較大的部署,不建議更改預設設定,
因為顯式地指定需要引用的bean 可以提供更好的控制和清晰度。
在某種程度上,它記錄了系統的結構。
byName 按屬性名稱自動裝配。
Spring尋找與需要自動實現的屬性同名的bean。
例如,如果一個bean定義autowire模式設定為byName,
並且它包含一個master屬性(也就是說,它有一個setMaster(..)方法)
,那麼Spring將查詢一個名為master的bean定義,
並使用它來設定該屬性。
主要還是根據set方法來確定屬性名,如果你有master屬性,
但是你的set方法是setMyMaster(..),
那麼Spring會查詢名為 myMaster的bean而不是名為 master的bean
byType 適用於set 方法的入參型別,
如果容器中恰好存在該屬性型別的一個bean,則允許該屬性自動注入。
如果存在多個,就會丟擲一個致命異常,
這表明您不能對該bean使用byType自動裝配。
如果沒有匹配的bean,則什麼也不會發生(沒有設定屬性)。
constructor 類似於byType,但適用於建構函式引數。
如果容器中沒有建構函式引數型別的確切bean,就會引發致命錯誤。

使用byType或constructor自動裝配模式,您可以自動注入arrays和collections型別。在這種情況下,將提供容器中與期望型別匹配的所有自動裝配候選,以滿足相關性。
如果接收的map 的key值型別為String,那麼你也可以讓Spring自動裝配Map型別的值,並且 Map例項的key值為相應的bean名稱。

	@Autowired
	private List<CusService> serviceLists;

	@Autowired
	private Map<String,CusService> cusServiceMap;
自動接線的侷限性和缺點

自動裝配在專案中一致使用時工作得最好。如果自動裝配沒有被普遍使用,那麼使用它來連線一個或兩個bean定義可能會使部分開發人員感到困惑。

考慮自動裝配的侷限性和缺點:

  • 屬性和構造引數設定中的顯式依賴關係總是覆蓋自動裝配。您不能自動連線簡單屬性,如primitives(boolean,int,long等)、String和classes(以及此類簡單屬性的陣列)。這種限制被刻意設計的。
  • 自動裝配bean不如顯式指定bean精確。不過,正如前面的表中所指出的,Spring已經儘可能避免產生意外結果。Spring管理的物件之間的關係不再被明確地記錄。
  • 對於能從Spring容器生成文件的工具來說生成連線資訊是不可能的了。
  • 自動注入依賴項時如果有多個可以匹配的選項,如果注入型別是陣列、集合或對映例項,這不是問題。但是,對於期望使用單個值的依賴項,這種模糊性不能任意解決。如果沒有可用的唯一bean定義,則丟擲異常。

在後一種情況下,您有幾個選項:

  • 放棄自動裝配,支援顯式佈線。
  • 通過將bean定義的autowire-candidate設定為false來避免自動裝配。
  • 通過將單個bean定義的元素的primary設定為true,將其指定為主候選bean定義。
  • 使用基於註釋的配置實現更細粒度的控制,1.9小節會專門講解註解使用。
1.放棄自動裝配,改成指定bean注入 @Qualifier 或者 xml的ref屬性都可以
2.
    	<bean id="serviceOne"  class="org.springframework.example.service.ServiceOne" />
    	<bean id="serviceTwo"  class="org.springframework.example.service.ServiceTwo" />
    兩個bean都繼承同一個介面CusService,如果有自動裝配如下
    	@Autowired
    	private CusService service;
    則啟動時候會報錯
    如果給serviceOne增加屬性autowire-candidate="false" 
    	<bean id="serviceOne"  class="org.springframework.example.service.ServiceOne" autowire-candidate="false" />
    則所有的自動裝配CusService的介面都會優先裝配serviceTwo
3.情況同2 還可以將serviceTwo 增加primary="true" 
        <bean id="serviceTwo"  class="org.springframework.example.service.ServiceTwo" primary="true" />
從自動裝配中排除Bean

在每個bean的基礎上,您可以從自動裝配中排除一個bean。在Spring的XML格式中,將元素的autowire-candidate設定為false。容器使得特定的bean定義對自動裝配基礎設施不可用(包括註釋風格配置,如@Autowired)。

autowire-candidate屬性被設計為隻影響基於型別的自動裝配。
它不影響按名稱的顯式引用,即使指定的bean沒有標記為自動裝配候選,
也會解析顯式引用。因此,如果名稱匹配,按名稱自動裝配仍然會注入一個bean。

元素在其default-autowire-candidates屬性接收一個patterns 字串,意思是根據patterns 字串匹配到的所有合格的beanName的autowire-candidates會被設定為true,不合格的會被設定為false
patterns 字串接受一個或多個匹配模式,多個patterns 字串之間可以用逗號隔開。 例如 (Repository,Service,*Dao) 這種組合模式

bean本身的autowire-candidates屬性優於的default-autowire-candidates屬性生效

這些技術對於那些您永遠不希望通過自動裝配被注入到其他bean中的bean非常有用。這並不意味著被排除的bean本身不能使用自動裝配進行配置。相反,該bean本身僅僅是不作為其他bean的自動連線候選物件。

	    public static final String AUTOWIRE_CANDIDATE_ATTRIBUTE = "autowire-candidate";

        //獲取autowire-candidate  這個值預設就是true
        String autowireCandidate = ele.getAttribute(AUTOWIRE_CANDIDATE_ATTRIBUTE);
        //判斷 是不是定義的當前bean是不是 default
		if (isDefaultValue(autowireCandidate)) {
		    //如果是default 
		    //查詢當前bean 所在beans的default-autowire-candidates屬性 找到配置的patterns表示式
		    //如果表示式為空 就不處理setAutowireCandidate屬性值 這樣該屬性依舊是true
			String candidatePattern = this.defaults.getAutowireCandidates();
			if (candidatePattern != null) {
			    //表示式不為空 判斷當前beanName 是否在表示式範圍內
			    //在範圍內就setAutowireCandidate設定為true
			    //否則設定為false
				String[] patterns = StringUtils.commaDelimitedListToStringArray(candidatePattern);
				bd.setAutowireCandidate(PatternMatchUtils.simpleMatch(patterns, beanName));
			}
		}
		else {
		//如果autowireCandidate 不是 default 是true 那就設定為true 
		//是false 那就設定為false
			bd.setAutowireCandidate(TRUE_VALUE.equals(autowireCandidate));
		}
1.4.6。方法注入

在大多數應用場景中,容器中的大多數bean是 singletons。
當單例Bean需要與另一個單例Bean協作或非單例Bean需要與另一個非單例Bean協作時,通常可以通過將一個Bean定義為另一個Bean的屬性來處理依賴性,生命週期相同的類互相注入時沒有問題。
當bean的生命週期不同時會出現問題。假設單例bean A需要使用非單例(原型)bean B,也許在A的每個方法呼叫上都使用。容器僅建立一次單例bean A,因此只有一次機會來設定屬性。每次需要一個容器時,容器都無法為bean A提供一個新的bean B例項。

一個解決方案是放棄某些控制反轉。您可以通過實現介面ApplicationContextAware ,並使每次容器 A都需要容器 B 的呼叫來請求(通常是新的)bean B例項,從而使bean A知道容器。以下示例顯示了此方法:ApplicationContextAware.getBean("B")

// a class that uses a stateful Command-style class to perform some processing
package fiona.apple;

// Spring-API imports
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

public class CommandManager implements ApplicationContextAware {

    private ApplicationContext applicationContext;

    public Object process(Map commandState) {
        // grab a new instance of the appropriate Command
        Command command = createCommand();
        // set the state on the (hopefully brand new) Command instance
        command.setState(commandState);
        return command.execute();
    }

    protected Command createCommand() {
        // notice the Spring API dependency!
        return this.applicationContext.getBean("command", Command.class);
    }

    public void setApplicationContext(
            ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}

前面的內容是不理想的,因為業務程式碼知道並耦合到Spring框架。方法注入是Spring IoC容器的一項高階功能,使您可以乾淨地處理此用例。

您可以在此部落格條目中瞭解有關方法注入動機的更多資訊。

Lookup Method注入

Lookup Method注入是指容器覆蓋容器管理bean上的方法並返回容器中另一個已命名bean的查詢結果的能力。查詢通常涉及原型bean,如上一節所述的場景。Spring框架通過使用來自CGLIB庫的位元組碼生成動態生成覆蓋該方法的子類來實現這種方法注入。

  • 要使這個動態子類工作,Spring bean容器子類的類不能是final,要覆蓋的方法也不能是final。
  • 單元測試具有抽象方法的類需要您自己建立類的子類,並提供抽象方法的存根實現。
  • 具體的方法對於元件掃描也是必要的,這需要具體的類來拾取。
  • 另一個關鍵的限制是,Lookup Method 不能與工廠方法一起工作,特別是與配置類中的@Bean方法一起工作,因為在這種情況下,容器不負責建立例項,因此不能動態地建立執行時生成的子類。

對於CommandManager前面的程式碼片段中的類,Spring容器動態地覆蓋該createCommand() 方法的實現。該CommandManager班沒有任何Spring的依賴,如下所示:

package fiona.apple;
public abstract class CommandManager {

    public Object process(Object commandState) {
        // 獲取適當的命令介面的新例項
        Command command = createCommand();
        // 在(希望是全新的)命令例項上設定狀態
        command.setState(commandState);
        return command.execute();
    }

    // 實現類在哪呢???
    protected abstract Command createCommand();
}

在包含要注入的方法的客戶端類中(本例為CommandManager),要注入的方法需要以下形式的簽名:

<public|protected> [abstract] <return-type> theMethodName(no-arguments);

如果方法為abstract,則動態生成的子類將實現該方法。否則,動態生成的子類將覆蓋原始類中定義的具體方法。考慮以下示例:

<!-- 作為原型部署的有狀態bean(非單例)-->
<bean id="myCommand" class="fiona.apple.AsyncCommand" scope="prototype">
    <!-- inject dependencies here as required -->
</bean>

<!-- commandProcessor使用statefulCommandHelper-->
<bean id="commandManager" class="fiona.apple.CommandManager">
    <lookup-method name="createCommand" bean="myCommand"/>
</bean>

標識為commandManager的bean在需要myCommand bean的新例項時呼叫它自己的createCommand()方法。是否要將myCommand bean部署為原型,必須仔細確認自己的需求。如果是單例,則每次都返回相同的myCommand bean例項。

或者,在基於註釋的元件模型中,您可以通過@Lookup註釋宣告一個查詢方法,如下面的示例所示:

public abstract class CommandManager {
    public Object process(Object commandState) {
        Command command = createCommand();
        command.setState(commandState);
        return command.execute();
    }

    @Lookup("myCommand")
    protected abstract Command createCommand();
}

或者,更慣用的是,您可以依賴於目標bean根據查詢方法的宣告的返回型別來解析:

public abstract class CommandManager {
    public Object process(Object commandState) {
        MyCommand command = createCommand();
        command.setState(commandState);
        return command.execute();
    }

    @Lookup
    protected abstract MyCommand createCommand();
}

請注意,您通常應該使用具體的存根實現來宣告這種帶註釋的查詢方法,以便它們與Spring的元件掃描規則相容,其中抽象類在預設情況下會被忽略。此限制不適用於顯式註冊或顯式匯入的bean類。

訪問範圍不同的目標bean的另一種方法是ObjectFactory/ Provider注入點(https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/core.html#beans-factory-scopes-other-injection)。


您可能還會發現ServiceLocatorFactoryBean(在 org.springframework.beans.factory.config包裝中)有用。
任意方法替換

方法替換注入的形式是用另一個方法實現替換託管bean中的任意方法的能力。這個十分不常用,您可以跳過本節的其餘部分,等到您真正需要此功能再來看。

對於基於xml的配置後設資料,您可以使用replaced-method元素將一個已部署bean的現有方法實現替換為另一個方法實現。考慮下面的類,它有一個名為computeValue的方法,我們想要覆蓋它:

public class MyValueCalculator {
    public String computeValue(String input) {
        // some real code...
    }
}

實現該org.springframework.beans.factory.support.MethodReplacer 介面的類提供了新的方法定義,如以下示例所示:

/**
 *用於覆蓋現有的computeValue(String input)
 *實現在MyValueCalculator
 */
public class ReplacementComputeValue implements MethodReplacer {

    public Object reimplement(Object o, Method m, Object[] args) throws Throwable {
        //獲取輸入值,使用它,並返回計算結果
        String input = (String) args[0];
        ...
        return ...;
    }
}

用於部署原始類並指定方法覆蓋的Bean定義類似於以下示例:

<bean id="myValueCalculator" class="x.y.z.MyValueCalculator">
    <replaced-method name="computeValue" replacer="replacementComputeValue">
        <arg-type>String</arg-type>
    </replaced-method>
</bean>

<bean id="replacementComputeValue" class="a.b.c.ReplacementComputeValue"/>

您可以在元素內使用一個或多個元素 來指示要覆蓋的方法的方法簽名。僅當方法過載且類中存在多個變體時,才需要對引數簽名。為了方便起見,引數的型別字串可以是完全限定型別名稱的子字串。
例如,以下所有都是java.lang.String匹配項 :

java.lang.String
String
Str

因為引數的數量通常足以區分每個可能的選擇,所以通過讓您僅鍵入與引數型別匹配的最短字串,此快捷方式可以節省很多輸入

相關文章