<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??一站式輕松地調用各大LLM模型接口,支持GPT4、智譜、豆包、星火、月之暗面及文生圖、文生視頻 廣告
                ## 4.4 依賴 典型的企業級應用程序不是由單獨對象(或 Spring 中的 bean)構成的。盡管最簡單的 應用程序有一些對象協同工作來表示終端用戶所看到的連貫的應用。下一節會解釋如何去為 完全實現的應用程序定義一組獨立的 bean,其中對象間相互協作來達到目標。 ### 4.4.1 依賴注入 依賴注入(DI)是對象定義它們依賴的過程,也就是說,要和它們協同工作其它對象, 僅僅可以通過構造方法參數,工廠方法參數,或者是在工廠方法返回的對象或被構造好后, 為對象實例設置的屬性。容器當創建好 bean,隨后就會注入那些依賴。這個過程從根本上 來說是反向的,因此命名為控制反轉(IoC),bea n 本身直接使用構造好的類或服務定位器 模式來控制實例或它的依賴的所在位置。 應用了 DI 原則,代碼就干凈多了,當為對象提供它們依賴的時候,解耦是很有效率的。 對象不再去檢查它的依賴,也不需要知道位置或依賴的類。因此,類就很容易被測試,特別 是當依賴是接口或抽象基類的時候,這允許在單元測試中使用 stub 或 mock 的實現類。 DI 存在兩種主要的方式,基于構造方法的依賴注入(4.4.1.1 節)和基于 setter 方法的依賴注入(4.4.1.2 節)。 #### 4.4.1.1 基于構造方法的依賴注入 基于構造方法的依賴注入是容器調用構造方法和一組參數完成的,每個都表示著一個依 賴。使用特定的參數來調用 static 工廠方法構造 bean 基本也是相同的,這種說法把給構 造方法的參數和給 static 工廠方法的參數相類似。下面的示例展示了一個僅使用構造方 法進行依賴注入的餓類。注意這個類沒有什么特殊之處,就是一個 POJO 而且沒有對容器特 定接口,基類或注解的依賴。 ``` public class SimpleMovieLister { // SimpleMovieLister對MovieFinder有依賴 private MovieFinder movieFinder; // 這個構造方法使得Spring容器可以’注入’MovieFinder public SimpleMovieLister(MovieFinder movieFinder) { this.movieFinder = movieFinder; } // 業務邏輯就可以’使用’注入的MovieFinder了,代碼就省略了... } ``` 構造方法參數解析 構造方法參數解析匹配使用參數的類型。如果在 bean 的構造方法參數不存在潛在的歧義,那么當 bean 被實例化的時候,定義的構造方法參數的順序就是被提供到適當構造方法 參數的順序。請看下面的類: ``` package x.y; public class Foo { public Foo(Bar bar, Baz baz) { // ... } } ``` 沒有潛在的歧義存在,假設 Bar 和 Baz 類沒有繼承關系。因此下面的配置就可以使用 了,而且并不需要明確地在`<constructor-arg/>`元素中去指定構造方法參數的索引和/ 或類型。 ``` <beans> <bean id="foo" class="x.y.Foo"> <constructor-arg ref="bar"/> <constructor-arg ref="baz"/> </bean> <bean id="bar" class="x.y.Bar"/> <bean id="baz" class="x.y.Baz"/> </beans> ``` 當另外一個 bean 被引用時,類型是明確的,那么匹配就能成功(前面示例中也是這樣 的)。當使用簡單類型時,比如`<value>true<value>`,Spring 不能決定值的類型,所以 沒有幫助是不能匹配的。看下面的示例: ``` package examples; public class ExampleBean { //計算最佳答案的年數 private int years; // 生命,宇宙,所有問題的答案 private String ultimateAnswer; public ExampleBean(int years, String ultimateAnswer) { this.years = years; this.ultimateAnswer = ultimateAnswer; } } ``` 構造方法參數類型匹配 在上面的情形下,如果你使用 type 屬性明確地為構造方法參數指定類型的話,容器可 以進行匹配簡單的類型。比如: ``` <bean id="exampleBean" class="examples.ExampleBean"> <constructor-arg type="int" value="7500000"/> <constructor-arg type="java.lang.String" value="42"/> </bean> ``` 構造方法參數索引 使用 index 屬性來指定明確的構造方法參數索引。比如: ``` <bean id="exampleBean" class="examples.ExampleBean"> <constructor-arg index="0" value="7500000"/> <constructor-arg index="1" value="42"/> </bean> ``` 此外,為了解決多個簡單值的歧義,如果構造方法有兩個相同類型的參數時,指定所以 可以解決歧義。注意索引是基于 0 開始的。 構造方法參數名稱 在 Spring 3.0 中,你也可以使用構造方法參數名稱來消除歧義: ``` <bean id="exampleBean" class="examples.ExampleBean"> <constructor-arg name="years" value="7500000"/> <constructor-arg name="ultimateanswer" value="42"/> </bean> ``` 要記住要使得這種方式可用,代碼必須和內嵌的調試標識一起編譯,那樣 Spring 才可 以從構造方法中來查找參數。如果沒有和調試標識(或不想)一起編譯,那么可以使用 JDK 的注解[@ConstructorProperties](http://download.oracle.com/javase/6/docs/api/java/beans/ConstructorProperties.html) 來明確構造方法參數。那么示例代碼就如下所示: ``` package examples; public class ExampleBean { // 忽略屬性 @ConstructorProperties({"years", "ultimateAnswer"}) public ExampleBean(int years, String ultimateAnswer) { this.years = years; this.ultimateAnswer = ultimateAnswer; } } ``` #### 4.4.1.2 基于 setter 方法的依賴注入 基于 setter 方法的依賴注入由容器在調用過無參數的構造方法或無參數的 static 工廠 方法來實例化 bean 之后,再調用 bean 的 setter 方法來完成的。 下面的示例展示了一個僅僅能使用純 setter 方法進行依賴注入的類。這是一個常見的 Java 類,是一個沒有依賴容器指定的接口,基類或注解的 POJO。 ``` public class SimpleMovieLister { // SimpleMovieLister對MovieFinder有依賴 private MovieFinder movieFinder; // setter方法可以讓Spring容器來'注入'MovieFinder public void setMovieFinder(MovieFinder movieFinder) { this.movieFinder = movieFinder; } // 真正’使用’注入的MovieFinder的業務邏輯代碼被省略了... } ``` ApplicationContext 對它管理的 bea n 支持基于構造方法和 setter 方法的依賴注入。 也 支 持 在 一 些 依 賴 已 經 通 過 構 造 方 法 注 入 之 后 再 進 行 setter 方 法 注 入 。 以 BeanDefinition 形式來配置依賴,使用 PropertyEditor 實例將屬性從一種形式格式 化到另一種。但很多 Spring 的用戶不直接(編程時)使用這些類,而是使用 XML 文件來定 義,之后會在內部轉換這些類的實例,需要加載整個 Spring IoC 容器的實例。 基于構造方法還是 setter 方法進行依賴注入? 因為你可以將二者混淆,那么對于基于構造方法或是 setter 方法的依賴注入,有一個很好的規則就是為強制依賴使用構造方法參數,而對于可選參數使用 setter 方法。要注意在 setter 方法上使用注解@Required(4.9.1 節),可用于 setter 方法所需的依賴。 Spring 團隊通常主張使用 setter 方法注入,因為大量的構造方法參數會使程序變得非 常笨拙,特別是當屬性為可選的時候。Setter 方法會讓該類的對象今后適合于重新配置或 重新注入。通過 JMX Mbean(第 23 章)來管理就是一個令人關注的用例。 一些人純粹贊成構造方法注入。提供所有對象的依賴意味著對象在完全初始化狀態 時,通常要返回客戶端(調用)代碼。這個缺點會使得對象變得不適合去重新配置或重新注入。 使用依賴注入的時候要特別注意一些類。當要選擇處理沒有源代碼時的第三方類庫 的時候。遺留的類可能沒有暴露任何 setter 方法,而構造方法注入則是唯一可用的依賴 注入方式。 #### 4.4.1.3 解決依賴過程 容器按如下步驟來解決 bean 的依賴: 1. ApplicationContext 和描述了所有 bean 的配置元數據一起被創建并初始化。配 置元數據可以通過 XML,Java 代碼或注解來指定。 2. 對于每一個 bean 來說,它的依賴被表述為屬性,構造方法參數的形式,如果你使用 了靜態工廠方法來代替構造方法,那么還會是靜態工廠方法參數的形式。當 bean 被實際創 建時,這些依賴被提供給 bean。 3. 每個屬性或構造方法參數就是一個要被設置的值或者是容器中其它 bean 的引用。 4. 每個屬性或構造方法參數值會被轉換特定格式的形式,去匹配屬性或構造方法參數的類型。默認情況下,Spring 可以轉換給定的字符串格式的值到內建的類型,比如 int,long, String,boolean 等。 當容器被創建時,Spring 容器會來驗證每個 bean 的配置,包括驗證 bean 的引用屬性是 否是一個合法的 bean。然而,bean 屬性本身直到 bean 真正被創建出來后才被設置進去。 當容器被創建時,bean 的范圍是單例時,會被設置成預實例(默認情況)來創建。范圍在 4.5 節,“bean 的范圍”部分來解釋。否則,bean 就會當被請求的時候被創建。一個 bean 的創建潛在地會引起一系列 bean 被創建,因為 bean 的依賴和它依賴的依賴(等等)要不 創建和定義出來。 循環依賴 如果你使用主要的構造方法注入,就可能會引起不可解決的循環依賴情形。 比如,類 A 需要通過構造方法注入獲得類 B 的實例,而類 B 也需要通過構造方法注入獲得類 A 的實例。如果把類 A 和類 B 進行相互注入,Spring 的 IoC 容器會在運行時檢 測到這是循環引用的情況,并且拋出 BeanCurrentlyInCreationException 異常。 一個可行的方案是編輯一些要配置的類的源碼,通過 setter 方法而不是構造方法進 行注入。而且,避免構造方法注入并僅僅使用 setter 方法注入。換句話說,盡管這不是推薦做法,你可以通過 setter 方法注入來配置循環依賴。 不像典型的用例(沒有循環依賴),在 bean A 和 bean B 之間的循環依賴強制一個 bean 被注入到另一個中會先于被完全初始化自己(就是經典的雞和蛋的問題)。 通常情況下你可以信任 Spring 做出正確的事情。它會在容器加載時來檢測配置問題, 比如引用了一個不存在的 bean 和循環依賴問題。當 bean 被實際創建出來后,Spring 設置屬 性和解決依賴會盡量地晚些。這就意味著 Spring 容器已經加載正確了但晚些時候可能會生 成異常,比如當你請求一個創建時發生問題的對象或者是它的依賴發送問題。例如,bean 因為丟失或非法屬性而拋出異常。這種潛在的一些配置問題的可見度延遲也就是為什么 ApplicationContext 的實現類默認情況都是預實例的單例 bean。在前期的時間和內存 消耗中,在 bean 真正需要前來創建這些 bean,當 ApplicationContext 被創建的時候, 你會發現這些配置問題,這還不晚。你也可以覆蓋這個默認的行為,那么單例 bean 就會延 遲加載,而不是預實例的了。 如果沒有循環依賴的存在,當一個或多個協作的 bean 被注入到一個獨立的 bean 時, 每個協作的 bean 就會在被注入之前完全被配置好。這就意味著如果 bean A 對 bean B 有依 賴,那么 Spring 的 IoC 容器會完全配置 bean B 而優先于調用 bean A 中的 setter 方法。換句 話說,bean 被實例化了(而不是預實例的單例 bean),它的依賴才被注入,相關的生命周 期方法(比如配置初始化方法(4.6.1.1 節)或 InitializingBea n 的回調方法(4.6.1.1 節))才 被調用。 #### 4.4.1.4 依賴注入示例 下面的示例使用了基于 XML 的配置元數據來進行基于 setter 方法的依賴注入。Spring XML 配置文件的一小部分來指定幾個 bean 的定義: ``` <bean id="exampleBean" class="examples.ExampleBean"> <!-- 使用嵌入<ref/>的元素來進行setter方法注入<ref/> --> <property name="beanOne"> <ref bean="anotherExampleBean"/> </property> <!-- 使用整潔的'ref'屬性來進行setter方法注入--> <property name="beanTwo" ref="yetAnotherBean"/> <property name="integerProperty" value="1"/> </bean> <bean id="anotherExampleBean" class="examples.AnotherBean"/> <bean id="yetAnotherBean" class="examples.YetAnotherBean"/> public class ExampleBean { private AnotherBean beanOne; private YetAnotherBean beanTwo; private int i; public void setBeanOne(AnotherBean beanOne) { this.beanOne = beanOne; } public void setBeanTwo(YetAnotherBean beanTwo) { this.beanTwo = beanTwo; } public void setIntegerProperty(int i) { this.i = i; } } ``` 上面的示例中,setter 方法被聲明為匹配 XML 文件中指定的屬性。下面的示例使用基于 構造方法的依賴注入: ``` <bean id="exampleBean" class="examples.ExampleBean"> <!-- 使用嵌入的<ref/>元素進行構造方法注入 --> <constructor-arg> <ref bean="anotherExampleBean"/> </constructor-arg> <!-- 使用整潔的'ref'屬性來進行構造方法注入 --> <constructor-arg ref="yetAnotherBean"/> <constructor-arg type="int" value="1"/> </bean> <bean id="anotherExampleBean" class="examples.AnotherBean"/> <bean id="yetAnotherBean" class="examples.YetAnotherBean"/> public class ExampleBean { private AnotherBean beanOne; private YetAnotherBean beanTwo; private int i; public ExampleBean(AnotherBean anotherBean, YetAnotherBean yetAnotherBean, int i) { this.beanOne = anotherBean; this.beanTwo = yetAnotherBean; this.i = i; } } ``` 在 bean 中指定的構造方法參數會被用于 ExampleBean 的構造方法參數。 現在考慮一下這個示例的變化情況,要代替使用構造方法,Spring 被告知調用 static 工廠方法并返回一個對象的實例: ``` <bean id="exampleBean" class="examples.ExampleBean" factory-method="createInstance"> <constructor-arg ref="anotherExampleBean"/> <constructor-arg ref="yetAnotherBean"/> <constructor-arg value="1"/> </bean> <bean id="anotherExampleBean" class="examples.AnotherBean"/> <bean id="yetAnotherBean" class="examples.YetAnotherBean"/> public class ExampleBean { // 私有的構造方法 private ExampleBean(...) { ... } // 靜態工廠方法; 這個方法的參數可以被認為是要返回bean的依賴, // 而不管那些參數是如何被使用的。 public static ExampleBean createInstance (AnotherBean anotherBean, YetAnotherBean yetAnotherBean, int i) { ExampleBean eb = new ExampleBean (...); // 其它的操作... return eb; } } ``` static 工廠方法的參數通過`<constructor-arg/>`元素來提供,這和構造方法已經 被實際調用是完全一致的。由工廠方法返回的類的類型不需要和包含 static 工廠方法類 的類型相同,盡管在本例中是相同的。實例(非靜態)工廠方法可以被用于本質上相同的方 式(除了 factory-bean 屬性的使用,來代替 class 屬性),所以這里不討論它們的細節。 ### 4.4.2 深入依賴和配置 正如之前章節中所提到的,你可以定義 bean 的屬性和構造方法參數作為其它被管理 bean(協作者)的引用,或者作為內聯值的定義。出于這個目的,Spring 的基于 XML 的配 置元數據支持使用`<property/>`和`<constructor-arg/>`元素的子元素類型。 #### 4.4.2.1 直接值(原生類型,String,等) <property/>元素的 value 屬性指定了屬性或構造方法參數,這是人們可以閱讀的 字符串的表達。正如之前提到過的(6.4.2 節),JavaBean 的 PropertyEditor 可以用來轉 換這些字符串值從 String 到屬性或參數的真實類型。 ``` <bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <!-- setDriverClassName(String)方法調用的結果 --> <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="masterkaoli"/> </bean> ``` 對于更簡潔的 XML 配置,下面的示例使用了 p-命名空間(4.4.2.6 節)。 ``` <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 http://www.springframework.org/schema/beans/spring -beans-3.0.xs d"> <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="masterkaoli"/> </beans> ``` 上面的 XML 非常簡潔;然而,錯誤之處會在運行時被發現而不是設計的時候,除非在 你創建 bean 的時候,使用如 [IntelliJ IDEA](http://www.jetbrains.com/idea/) [或](http://www.springsource.com/products/sts) [SpringSource Tool Suite](http://www.springsource.com/products/sts)(STS,SpringSource 組織 開發的工具套件)這樣的 IDE 來支持自動地屬性補全。這樣的 IDE 幫助是強烈建議使用的。 你也可以這樣來配置 java.util.Properties 實例: ``` <bean id="mappings" class="org.springframework.beans.factory.config.PropertyPlac eholderConfigurer"> <!-- 作為java.util.Properties類型 --> <property name="properties"> <value> jdbc.driver.className=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/mydb </value> </property> </bean> ``` Spring 容器會使用 JavaBean 的 PropertyEditor 機制來轉換`<value/>`元素中的文本 到 java.util.Properties 實例。這是一個很好的捷徑,也是 Spring 團隊少有喜歡使用 嵌套的`<value/>`元素而不是 value 屬性方式的地方之一。 idref 元素 idref 元素是一種簡單防錯的形式來傳遞容器中另外一個 bean 的 id(字符串值而不是引用)到`<constructor-arg/>`或`<property/>`元素中。 ``` <bean id="theTargetBean" class="..."/> <bean id="theClientBean" class="..."> <property name="targetName"> <idref bean="theTargetBean" /> </property> </bean> ``` 上面定義 bean 的代碼片段是和下面的片段完全等價(在運行時): ``` <bean id="theTargetBean" class="..." /> <bean id="client" class="..."> <property name="targetName" value="theTargetBean" /> </bean> ``` 第一種形式比第二種形式可取,因為使用 idref 標簽允許容器在部署時來驗證被引用 的,命名的 bean 是否真的存在。第二種形式下,沒有對傳遞給 client bea n 的 targetName 屬性執行驗證。錯誤僅僅是在 client bean 被真正實例化的時候才會被發現(和很多可能 致命的結果)。如果 client bean 是 prototype(4.5 節)類型的 bean,這個錯誤和結果異常 可能僅僅在容器被部署很長一段時間后才會被發現。 此外,如果被引用的 bean 在同一個 XML 單元中,bean 的名稱就是 bean 的 id,那么你 還可以使用 local 屬性,這允許 XML 解析器本身在 XML 文檔解析的時候,及早地來驗證 bean 的 id。 ``` <property name="targetName"> <!-- id為'theTargetBean'的bean必須存在;否則就會拋出異常 --> <idref local="theTargetBean"/> </property> ``` `<idref/>` 元 素 帶 來 的 一 個 相 同 之 處 ( 最 少 是 Spring 2.0 版 本 以 前 的 ) 是 值 在 ProxyFactoryBean 定義的 AOP 攔截器的配置中。當你指定攔截器名稱來防止拼錯攔截 器的 id 時,可以使用`<idref/>`元素。 #### 4.4.2.2 引用其它 bean(協作者) ref 元素是`<constructor-arg/>`或`<property/>`定義元素中最后的一個。在這里 你可以為 bean 設置指定屬性的值,來引用被容器管理的另外一個 bean(協作者)。被引用 的 bean 就是要設置屬性的這個 bean 的一個依賴,并且是初始化的作為屬性設置之前的點 播。(如果協作者是一個單例的 bean,它可能已經被容器初始化過了。)所有引用最終都會 被引用到一個對象中。范圍和驗證基于通過 bean,local 或 parent 屬性指定 id/name 的其它對象。 通過`<ref/>`標簽的 bean 屬性來指定目標 bean 是最常用的方式,并允許創建引用到相 同容器或父容器的任意 bean 中,而不管它們是不是在同一個 XML 文件中。bean 屬性的值 可能和目標 bean 的 id 屬性相同,或者是目標 bean 的 name 屬性值之一。 ``` <ref bean="someBean"/> ``` 通過 local 屬性來指定目標 bean 是利用了 XML 解析器的能力,來驗證 XML 相同文件 內的 id 引用。local 屬性的值必須和目標 bean 的 id 屬性一致。如果沒有在相同的文件內 發現匹配的元素,那么 XML 解析器會報告問題。因此,如果目標 bean 在同一個 XML 文件 中的話,使用 local 這種形式是最佳選擇(為了更早地知道錯誤)。 ``` <ref local="someBean"/> ``` 通過 parent 屬性來指定目標 bea n 會為 bea n 創建引用,它是在當前容器的父容器中的。parent 屬性的值可能和目標 bean 的 id 屬性相同,或者是目標 bean 的 name 屬性值 之一,而且目標 bean 必須在當前容器的父容器中。當有一個容器繼承關系,可以使用這個 bean 的引用,或在你想使用和父 bean 有相同名稱的代理包裝一個父容器中已有 bean 時。 ``` <!-- 在父上下文中--> <bean id="accountService" class="com.foo.SimpleAccountService"> <!-- 在這里插入所需要的依賴 --> </bean> <!-- 在子(后繼)上下文中 --> <bean id="accountService" <-- bean名稱和父bean相同 --> class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="target"> <ref parent="accountService"/> <!-- 注意我們如何引用父bean --> </property> <!-- 在這里插入其它配置和所需的依賴 --> </bean> ``` #### 4.4.2.3 內部 bean 在`<property/>`或`<constructor-arg/>`元素內部的`<bean/>`元素的定義被稱為內部 bean。 ``` <bean id="outer" class="..."> <!-- 為了替代為目標bean使用引用,簡單內聯定義目標bean --> <property name="target"> <bean class="com.example.Person"> <!-- 這就是內部bean --> <property name="name" value="Fiona Apple"/> <property name="age" value="25"/> </bean> </property> </bean> ``` 內部 bean 的定義不需要定義 id 或 name;容器忽略這些值。也會忽略 scope 標識。內 部 bean 通常是匿名的,而且范圍通常是 prototype(4.5.2 節)的。注入內部 bean 到協作 bean 是不可能的,只能到包圍它的 bean 中。 #### 4.4.2.4 集合 在 `<list/>` , `<set/>` , `<map/>` 和 `<props/>` 元素中,你可以分別設置 Java 的 Collection 類型 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 的值,也可以是下列元素之一: + bean + ref + idref + list + set + map + props + value + null 集合合并 在 Spring 2.0 中,容器支持集合的合并。應用開發人員可以定義父樣式的`<list/>`,`<map/>`,`<set/>`或`<props/>`元素,子樣式的`<list/>`,`<map/>`,`<set/>`或`<props/>`元素繼承或重寫來自父集合的值。也就是說,子集合的值是合并元素父與子集合中的元素,和子集合元素覆蓋父集合中指定值的結果。 本節關于導論父子 bean 合并的機制。如果讀者不熟悉父和子 bean 的定義,可能要先去 閱讀一下相關章節(4.7 節)。 下面的示例說明了集合合并: ``` <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"> <!-- 合并在*子*集合定義中來指定 definition --> <props merge="true"> <prop key="sales">sales@example.com</prop> <prop key="support">support@example.co.uk</prop> </props> </property> </bean> <beans> ``` 注意在 child bean 中的 adminEmails 屬性下的`<props/>`元素中的 merge=true 屬性的使用。當 child bean 被容器處理并實例化時,結果實例中有一個 adminEmails Properties 的 集 合 , 包 含 了 合 并 子 bean 的 adminEmails 集合和父 bean 的 adminEmails 集合。 ``` administrator=administrator@example.com sales=sales@example.com support=support@example.co.uk ``` 子 bean 的 Properties 集合的值設置繼承了從父`<props/>`元素而來的所有屬性,子 bean 中 support 的值覆蓋了父 bean 中的值。 這種合并行為的應用和`<list/>`,`<map/>`和`<set/>`集合類型很相似。在`<list/>`元素特 定的情形下,和 List 集合類型相關的語義,也就是說,ordered 集合值的概念,是要維 護的;父值優先于所有子 list 的值。在 Map,Set 和 Properties 集合類型的情況下,沒有順序的存在。因此對于容器內部使用的,和 Map,Set 和 Properties 實現類型相關的 集合類型沒有排序語義的作用。 集合合并的限制 不能合并不同類型的集合(比如 Map 和 List),如果你要嘗試這么去做,那么就會拋 出 Exception。merge 屬性必須在低級的,繼承的,子 bean 中來指定;在父集合中指定 merge 屬性是冗余的,也不會看到想要的合并結果。合并特性僅在 Spring 2.0 和更高版本中 可用。 強類型集合(Java 5 以上版本) 在 Java 5 或更高版本中,你可以使用強類型集合(使用泛型)。也就是說,可以聲明一 個 Collection 類型,它可以僅僅包含 String 元素(作為示例)。如果你使用 Spring 來對強類型的 Collection 依賴注入到 bean 中,你可以利用 Spring 的類型轉換來支持這樣 強類型 Collection 實例的元素,在被加到 Collection 之前,可以轉換成合適的類型。 ``` public class Foo { private Map<String, Float> accounts; public void setAccounts(Map<String, Float> accounts) { this.accounts = accounts; } } <beans> <bean id="foo" class="x.y.Foo"> <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> ``` 當 foo bean 的 accounts 屬性準備好注入時,關于強類型元素 `Map<String, Float>` 類型的泛型信息就通過反射機制準備好了。因此 Spring 的類型轉換工具識別到各種元素的 值作為 Float 類型,字符串值 9.99,2.75 和 3.99 被轉換成實際的 Float 類型。 #### 4.4.2.5 null 和空字符串 Spring 將屬性的空參數當作空 String。下面基于 XML 的配置元數據片段設置了電子郵 件屬性為空 String 值("")。 ``` <bean class="ExampleBean"> <property name="email" value=""/> </bean> ``` 上面的例子和下面的 Java 代碼是一樣的:exampleBean.setEmail("")。`<null/>` 元素控制 null 值。比如: ``` <bean class="ExampleBean"> <property name="email"><null/></property> </bean> ``` 上面的配置和下面的 Java 代碼一致:exampleBean.setEmail(null)。 #### 4.4.2.6 使用 p-命名空間的 XML 快捷方式 p-命名空間可以使你使用 bea n 的元素屬性,而不是內嵌的`<property/>`元素,來描述屬 性的值和/或協作的 bean。 Spring 2.0 和后續版本支持使用命名空間(附錄 C)的可擴展的配置格式,這是基于 XML的 Schema 定義的。本章中討論的 bea n 的配置格式是定義在 XML 的 Schema 下的。然而,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 http://www.springframework.org/schema/beans/spring -beans-3.0.xsd"> <bean name="classic" class="com.example.ExampleBean"> <property name="email" value=["foo@bar.co](mailto:foo@bar.com)m"/> </bean> <bean name="p-namespace" class="com.example.ExampleBean" p:email=["foo@bar.co](mailto:foo@bar.com)m"/> </beans> ``` 示例展示了 p-命名空間下的屬性,在 bean 的定義中稱為 email。這就告訴 Spring 來包 含屬性聲明。正如前面提到的,p-命名空間沒有 schema 定義,所以你可以設置屬性的名稱 和 bean 中屬性的名稱一樣。 下面的示例包含了兩個 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 http://www.springframework.org/schema/beans/spring -beans-3.0.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 中 使用了`<property name="spouse" ref="jane"/>`來創建 john bean 對 jane bean 的引用關系,第二個 bean 中,使用了 p:spouse-ref="jane"作為屬性來做相同的事情。本例中,spouse 是屬性名,而-ref 部分表明了這不是一個直接值而是一個對其它 bean 的引用。 > ![](https://box.kancloud.cn/2016-01-25_56a58526bb420.gif) > 注意 > P-命名空間沒有標準 XML 格式那么靈活。比如,聲明屬性引用的格式和以 Ref 結尾的 屬性相沖突,而標準的 XML 格式則不會。我們建議謹慎選擇所用的方法并和開發團隊成員 充分地交流,避免產生同時使用這三種方法的 XML 文檔。 #### 4.4.2.7 使用 c-命名空間的 XML 快捷方式 和 4.4.2.6 節,“使用 p-命名空間的 XML 快捷方式”相似,c-命名空間是在 Spring 3.1 中 被引入的,允許使用內聯屬性來配置構造方法參數而不是使用 constructor-arg 元素。 我們來看 4.4.1.1 節,“基于構造方法的依賴注入”中的示例,現在使用 c 命名空間: ``` <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema -instance" xmlns:p="http://www.springframework.org/schema/c" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="bar" class="x.y.Bar"/> <bean id="baz" class="x.y.Baz"/> <-- '傳統的'聲明方式 --> <bean id="foo" class="x.y.Foo"> <constructor-arg ref="bar"/> <constructor-arg ref="baz"/> <constructor-arg value=["foo@bar.co](mailto:foo@bar.com)m"/> </bean> <-- 'c-命名空間'聲明方式 --> <bean id="foo" class="x.y.Foo" c:bar-ref="bar" c:baz-ref="baz" c:email=["foo@bar.co](mailto:foo@bar.com)m"> </beans> ``` c:命名空間使用了和 p:(以-ref 結尾的 bean 引用)相同的轉換機制通過它們的名稱 來設置構造方法參數。這樣一來,即便沒有在 XSD schema(但是存在于 Spring 核心的內部) 中定義,它還是需要聲明出來。 在極少數的情況下,構造方法參數名稱是不可用的(通常如果字節碼在編譯時沒有調試 信息),我們可以使用參數索引: ``` <-- 'c-命名空間'索引聲明 --> <bean id="foo" class="x.y.Foo" c:_0-ref="bar" c:_1-ref="baz"> ``` > ![](https://box.kancloud.cn/2016-01-25_56a58526bb420.gif) > 注意 > 因為 XML 的語法,索引符號需要在其頭部使用_作為 XML 屬性名稱,因為 XML 中屬性 是不能以數字開頭的(盡管一些 IDE 允許這樣做)。 在實際運用中,構造方法解析機制(4.4.1.1 節)在匹配參數時是非常有效率的,所以除 非真有需要,我們建議在配置中使用名稱符號。 #### 4.4.2.8 復合屬性名稱 在設置 bean 的屬性時,只要路徑中的所有組件,除了最后一個屬性名稱是非 null 的, 可以使用復合或者嵌套的屬性名稱。參考下面的 bean 定義。 ``` <bean id="foo" class="foo.Bar"> <property name="fred.bob.sammy" value="123" /> </bean> ``` foo bean 有一個 fred 屬性,它還有一個 bob 屬性,而它仍有一個 sammy 屬性,而最 終的 sammy 屬性被設置成數值 123。為了讓這樣的配置可用,foo 的屬性 fred,fred 的 屬性 bob 在 bean 被構造好后必須不能為 null,否則會拋出 NullPointerException 異常。 ### 4.4.3 使用 depends-on 如果一個 bean 是另外一個 bean 的依賴,通常表明了它會被設置成另外一個的屬性。 典型的情況是在基于 XML 的配置元數據中使用`<ref/>`(4.4.2.2 節)元素來完成。然而,有 時在兩個 bean 之間的依賴并不是直接的;比如,類中的靜態初始化器需要觸發,就像數據 庫驅動程序的注冊。depends-on 屬性可以明確地強制一個或多個 bean 在使用這個元素的 bean 被初始化之前被初始化。下面的示例使用了 depends-on 屬性來表示一個獨立 bean 的依賴: ``` <bean id="beanOne" class="ExampleBean" depends-on="manager"/> <bean id="manager" class="ManagerBean" /> ``` 為了表示對多個 bean 的依賴,提供一個 bean 名稱的列表作為 depends-on 屬性的值 即可,要用逗號,空白或分號分隔開,它們都是有效的分隔符: ``` <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" /> ``` > ![](https://box.kancloud.cn/2016-01-25_56a58526bb420.gif) > 注意 > 在 bean 定義中的 depends-on 屬性可以指定初始化時的依賴,也可以是僅僅是單例(4.5.1 節)bean,對應銷毀時的依賴。和給定 bean 定義了 depends-on 關系的依賴 bean 首先被銷毀,先于給定的 bean 本身。因此 depends-on 也能控制關閉順序。 ### 4.4.4 延遲初始化 bean 默認情況下,ApplicationContext 的實現類積極地創建并配置所有單例的 bean (4.5.1 節),作為初始化過程的一部分。通常來說,這種預實例化是非常可取的,因為在配 置或周邊環境中的錯誤可以直接被發現,而不是幾小時或幾天后去發現。當這種行為不可用時,你可以阻止單例 bean 的預實例化,在 bean 定義中使用延遲初始化來標記一下就可以 了。延遲初始化 bean 告訴 IoC 容器在該 bean 第一次被請求時再來實例化,而不是在啟動時 實例化。 在 XML 中,這個行為可以通過`<bean/>`元素的 lazy-init 屬性來控制;比如: ``` <bean id="lazy" class="com.foo.ExpensiveToCreateBean" lazy-init="true"/> <bean name="not.lazy" class="com.foo.AnotherBean"/> ``` 當前者配置被 ApplicationContext 處理時,命名為 lazy 的 bean 并不會在 ApplicationContext 啟動時被預實例化,而 not.lazy bean 會被先預實例化。 而到延遲初始化 bean 是一個單例 bean 的依賴時,且這個單例 bean 不是延遲初始化的,那么 ApplicationContext 也會在啟動時來創建延遲初始化的 bean,因為它必須滿足單 例 bean 的依賴。延遲初始化的 bean 被注入到單例 bean 中的時候它就不是延遲初始化的了。 你也可以在容器級別來控制延 遲初始化,在 `<beans/>` 元素上使用default-lazy-init 屬性;比如: ``` <beans default-lazy-init="true"> <!-- 那么就沒有bean是預實例化的了... --> </beans> ``` ### 4.4.5 自動裝配協作者 Spring 容器可以自動裝配協作 bean 之間的關系。你可以允許 Spring 自動為你的 bean 來 解析協作者(其它 bean),通過檢查 ApplicationContext 的內容即可。自動裝配有下 列優勢: 自動裝配可以顯著減少指定屬性或構造方法參數的需要。(如 bean 模板的機制,這將在 本章的 4.7 節來討論,在這方面是有價值的。) 自動裝配可以更新配置對象的演變。比如,如果你需要給一個類添加依賴,那個依賴可 以自動被填入而不需要修改配置。因此,自動裝配在開發期間是非常有用的,當代碼庫 變得更穩定時切換到明確的裝配也沒有否定的選擇。 當使用基于 XML 的配置元數據(4.4.1 節)時,你可以為 bean 的定義指定自動裝配模 式,在`<bean/>`元素上設置 autowire 屬性即可。自動裝配功能有五種模式。你可以為每個 bean 來指定自動裝配,因此可以選擇為哪一個來指定自動裝配。 表 4.2 自動裝配模式 | 模式 | 解釋 | | --- | --- | | no | (默認情況)沒有自動裝配。Bean 的引用必須通過 ref 元素來定義。對于大型的部署,修改默認設置是不推薦的,因為明確地指定協作者會給予更多的控制和清晰。某種程度上來說,它勾勒出了系統的結構。 | | byName | 通過屬性名稱來自動裝配。Spring 以相同名稱來查找需要被自動裝配的 bean。比如,如果 bean 被設置成由名稱來自動裝配,并含有一個 master 屬性(也 就說,有 setMaster(..)方法),Spring 會查找名為 master 的 bean 定義,并且用它來設置屬性。 | | byType | 如果 bean 的屬性類型在容器中存在的話,就允許屬性被自動裝配。如果存在多于一個,就會拋出致命的異常,這就說明了對那個 bean 不能使用 byType 進行自動裝配。如果沒有匹配的 bean 存在,就不會有任何效果;屬性不會 被設置。 | | constructor | 和 byType 類似,但是是應用于構造方法參數的。如果在容器中沒有確定的構造方法參數類型的 bean 的存在,就會發生致命的錯誤。 | 使用 byType 或 constructor 自動裝配模式,你可以裝配數組和集合類型。這種情況下所有在容器內匹配期望類型的自動裝配候選者會被用來滿足依賴。如果期望的鍵類型是String,你可以自動裝配強類型的 Map。自動裝配的 Map 值會包括匹配期望類型的實例, 而且 Map 的鍵會包含對應 bean 的名稱。 你可以聯合自動裝配行為和依賴檢查,這會在自動裝配完成之后執行。 #### 4.4.5.1 自動裝配的限制和缺點 當在項目中一直使用時,自動裝配是非常不錯的。如果自動裝配通常是不使用的,它就 可能會迷惑開發人員來使用它去裝配僅僅一兩個 bean。 考慮一下自動裝配的限制和缺點: * 在 property 和 constructor-arg 中設置的明確依賴通常覆蓋自動裝配。不能自動 裝配所謂的簡單屬性,比如原生類型,String 和 Class(還有這樣簡單屬性的數字)。這 是由于設計的限制。 * 自動裝配沒有明確裝配那么精確,盡管,在上面的表格中已經說明了,Spring 很小心地 避免在有歧義的情況下去猜測,但也可能會有意想不到的結果,在 Spring 管理的對象 中間的關系可能就不會再清晰地說明了。 * 裝配信息可能對從 Spring 容器中來生成文檔的工具來說不可用。 * 在容器中的多個 bean 定義可能通過 setter 方法或構造方法參數匹配指定的類型進行自 動裝配。對于數組,集合或 Map,這不一定是一個問題。而對期望簡單值的依賴,這 種歧義不能隨意解決。如果沒有唯一的 bean 可用,就會拋出異常。 在后面一種情況中,你有幾種選擇: * 放棄自動裝配而使用明確的裝配。 * 避免設置它的 autowire-candidate 屬性為 false 來自動裝配 bean,這會在下一 節中來解釋。 * 設置`<bean/>`元素的 primary 屬性為 true 來指定單獨的 bean 作為主要的候選者。 * 如果你使用 Java 5 或更高版本,使用基于注解的配置實現更細粒度的控制,這會在 4.9 節,“基于注解的容器配置”中來解釋。 #### 4.4.5.2 從自動裝配中排除 bean 在每個 bean 的基礎上,你可以從自動裝配中來排除 bean。在 Spring 的 XML 格式配置 中,設置`<bean/>`元素的 autowire-candidate 屬性為 false;容器會把指定的 bean 對自動裝配不可用(包含注解風格的配置,比如@Autowired(4.9.2 節))。 你也可以基于 bean 名稱的模式匹配來限制自動裝配候選者。頂級的`<beans/>`元素中的 default-autowire-candidates 屬性接受一個或多個模式。比如,為了限制自動裝 配候選者到任意名稱以 Repository 結尾 bean 的狀態,提供*Repository 值。要提供多個模式,把它們定義在以逗號分隔的列表中。Bean 定義中 autowire-candidate 屬性的 true 或 false 明確的值通常是優先的,而且對于這些 bean 來說,模式匹配規則是不適用的。 這些技術對那些永遠不想通過自動裝配被注入到其它 bea n 中的 bea n 來說是很有用的。 這并不意味著未包含的 bean 不能使用自動裝配來配置。相反,bean 本身不是自動裝配其它 bean 的候選者。 ### 4.4.6 方法注入 在很多應用場景中,很多容器中的 bean 是單例(4.5.1 節)的。當一個單例的 bean 需 要和其它單例的 bean 協作時,或者一個非單例的 bean 需要和其它非單例的 bean 協作時, 典型地做法是通過定義一個 bean 作為另外一個的屬性來控制依賴。當 bean 的生命周期不 同時問題就產生了。假設單例 bean A 需要使用非單例(prototype,原型)bean B,或許在 A 的每個方法調用上。容器僅僅創建單例 bean A 一次,因此僅僅有一次機會去設置屬性。容 器不能每次為 bean A 提供所需的 bean B 新的實例。 解決方法就是放棄一些控制反轉。你可通過實現 ApplicationContextAware 接口 以讓 bean A 去感知容器(4.6.2 節),而且在每次 bean A 需要 bean B 的實例時,可以讓容器 調用 getBean(“B”)(4.2.3 節)去獲取(一個新的)bean B。下面的代碼就是這種方式的例子: ``` // 使用了有狀態的命令風格的類來執行一些處理 package fiona.apple; // 導入Spring的API 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) { // 抓取相應命令的新實例 Command command = createCommand(); // 在命令實例上(希望標記新的)設置狀態 command.setState(commandState); return command.execute(); } protected Command createCommand() { // 注意Spring API的依賴! return this.applicationContext.getBean("command", Command.class); } public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } } ``` 前面的做法是不可取的,因為這些業務代碼感知并耦合到了 Spring 框架中。方法注入, 這種 Spring IoC 容器中的有點先進的功能,允許以一種干凈的方式來處理這個用例。 你可以在[博客文章](http://blog.springsource.com/2004/08/06/method-injection/)中來閱讀更多關于方法注入的動機。 #### 4.4.6.1 查找方法注入 查找方法注入是容器在其管理的 bean 上來覆蓋方法的能力,來返回容器中其它命名 bean 的查找結果。查找,典型的情況是涉及原型 bean,這是在之前章節中描述的情景。Spring Framework 從 CGLIB 類庫中,通過使用字節碼生成機制實現了這種方法注入,來動態地生成 覆蓋方法的子類。 > ![](https://box.kancloud.cn/2016-01-25_56a58526bb420.gif) > 注意 > 要讓這些動態子類起作用,在類路徑下必須有 CGLIB 的 jar 文件。這些 Spring 容器中的 子類不能是 final 類型的,要被覆蓋的方法也不能是 final 類型的。而且,測試有 abstract 方法的類要求你自己去編寫類的子類并提供 abstract 方法的實現。最后,是 方法注入目標的對象還不能被序列化。 看一下之前代碼片段中的 CommandManager 類,你會看到 Spring 容器會動態地覆蓋 createCommand()方法的實現。CommandManager 類不會有任何 Spring 的依賴,在重新設計 的例子中你會看到: ``` package fiona.apple; // 沒有Spring API的導入! public abstract class CommandManager { public Object process(Object commandState) { // 抓取一個心得合適的Command接口的實例 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="command" class="fiona.apple.AsyncCommand" scope="prototype"> <!-- 注入所需要的依賴 --> </bean> <!-- commandProcessor使用statefulCommandHelper --> <bean id="commandManager" class="fiona.apple.CommandManager"> <lookup-method name="createCommand" bean="command"/> </bean> ``` 當需要 command bean 的新的實例時,標識為 commandManager 的 bean 調用它自己的 方法 createCommand()。部署 command bean 為原型的可必須要小心,那確實就是需要 的才行。如果被部署為單例的(4.5.1 節),那么每次會返回相同的 command bean 的實例。 > ![](https://box.kancloud.cn/2016-01-25_56a58526e359f.gif) > 提示 > 感 興 趣 的 讀 者 可 能 會 發 現 要 使 用 ServiceLocatorFactoryBean (在 org.springframework.beans.factory.config 包下)。在 ServiceLocatorFactoryBean 中的使用的方法和其它工具類是相似的,ObjectFactoryCreatingFactoryBean,但是它允許你去指定你自己的查找接口而不是 Spring 特定的查找接口。對這些類查詢一下 JavaDoc 文檔,還有[博客文章](http://blog.arendsen.net/index.php/2006/10/05/on-the-servicelocatorfactorybean-dlas-and-the-sustainability-of-code-and-design/)來獲取 ServiceLocatorFactoryBean 的額外的信息。 #### 4.4.6.2 任意方法的替代 在方法注入中,用處不如查找方法注入大的一種形式,就是使用另外一個方法實現來替 換被容器管理的 bean 中的任意方法的能力。用戶可以安全地跳過本節的其它部分,直到你 真正需要這些功能的時候來回過頭來看。 使用基于 XML 的配置元數據,對于部署的 bean,你可以使用 replaced-method 元 素來使用另外一個方法替換已有的方法實現。看看下面這個類,有一個我們想要去覆蓋的 computeValue 方法: ``` public class MyValueCalculator { public String computeValue(String input) { // 真正的代碼... } // 其它方法... } ``` 實現了 org.springframework.beans.factory.support.MethodReplacer 接口的類提供新的方法定義。 ``` /** 也就是要在MyValueCalculator中實現用來覆蓋已有的方法 computeValue(String) */ 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"/> ``` 你可以在`<replaced-method/>`元素中使用一個或多個包含的`<arg-type/>`元素來 指示被覆蓋方法的方法簽名。如果方法是被重載的并在類中有很多種形式的存在,那么參數 的簽名是必須的。為了簡便,字符串類型的參數可能是類型全名的一個子串。比如,下面所 有的都匹配 java.lang.String: ``` java.lang.String String Str ``` 因為用參數的數量用來區分每一個可能的選擇通常是足夠的,這種快捷方式可以節省大 量的輸入,允許你去輸入匹配參數類型的最短的字符串。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看