????? Spring1.1中引入了一個很有用的IOC新特性--方法注入(Method Injection)。這能讓我們在組裝bean時獲得極大的靈活性。Spring的方法注入可以分為兩種形式:查詢方法注入(Lookup Method Injection)和方法替換(Method Replacement)。查詢方法注入提供了另一種機制來讓bean獲取它依賴的其它bean,方法替換則可以在不修改源代碼的基礎(chǔ)上,修改任意bean的任何方法的實現(xiàn)。為了提供這兩個功能,Spring借助了CGLIB包。
??????先來看看查詢方法注入的使用。想象這樣的情形:我們有一個singleton類型的bean A,它需要使用另一個非singleton類型的bean B來執(zhí)行一些操作。由于兩個bean的生命周期是不同的,因此我們不能簡單的在bean A的配置中使用ref標(biāo)簽來要求Spring注入bean B,因為那樣會讓我們每次取得bean A時都使用同一個bean B的實例。回想前面介紹過的BeanFactoryAware接口,我們可以讓bean A實現(xiàn)該接口,這樣就可以在bean A內(nèi)部使用beanFactory的genBean方法來獲取bean B了。只要我們將bean B配置為非singleton類型,每次使用getBean方法就會得到一個新的bean B的實例。
??????使用查詢方法注入,我可以不必實現(xiàn)任何的Spring的接口,也不需要在bean A中顯示的使用getBean方法來獲得bean B。我們只需要在bean A中申明一個查詢方法,然后在bean A的配置文件中指明該查詢方法,那么Spring就會自動的將bean B注入到bean A中去了。由于查詢方法注入的概念相對比較復(fù)雜,因此我們還是通過具體的例子來直觀的感受它是如何工作的。
??????在這個例子中,我們創(chuàng)建一個非singleton的bean和兩個實現(xiàn)了同一個接口的singleton的bean,一個通過傳統(tǒng)的設(shè)值方法注入獲得非singleton的bean,另一個則通過查詢方法注入。
//The MyHelper Bean
public class MyHelper {
??? public void doSomethingHelpful() {
??????? // do something!
??? }
}
//The DemoBean Interface
public interface DemoBean {
??? public MyHelper getMyHelper();
??? public void someOperation();
}
//The StandardLookupDemoBean
public class StandardLookupDemoBean implements DemoBean {
??? private MyHelper helper;
??? public void setMyHelper(MyHelper helper) {
??????? this.helper = helper;
??? }
??? public MyHelper getMyHelper() {
??????? return this.helper;
??? }
??? public void someOperation() {
??????? helper.doSomethingHelpful();
??? }
}
//The AbstractLookupDemoBean
public abstract class AbstractLookupDemoBean implements DemoBean {
??? public abstract MyHelper getMyHelper();
??? public void someOperation() {
??????? getMyHelper().doSomethingHelpful();
??? }
}
<beans>
??? <bean id="helper" class="com.apress.prospring.ch5.mi.MyHelper" singleton="false"/>
??? <bean id="abstractLookupBean"?
class="com.apress.prospring.ch5.mi.AbstractLookupDemoBean">
??????? <lookup-method name="getMyHelper" bean="helper"/>
??? </bean>
??? <bean id="standardLookupBean"
??? class="com.apress.prospring.ch5.mi.StandardLookupDemoBean">
??????? <property name="myHelper">
??????????? <ref local="helper"/>
??????? </property>
??? </bean>
</beans>
?????
可以看到,我們使用了lookup-method來對查詢方法注入進(jìn)行配置,name屬性告訴Spring需要覆寫bean中的哪個方法,該方法必須是沒有參數(shù)的,并且返回類型是我們需要獲得的bean的類型,bean屬性告訴Spring查詢方法需要返回哪個bean。
????? 通過DemoBean standardBean = (DemoBean)factory.getBean("standardLookupBean");獲得standardBean,然后比較standardBean.getMyHelper() == standardBean.getMyHelper(),可以發(fā)現(xiàn),直接使用設(shè)置方法注入,每次獲得的非singleton的bean實際上是同一個實例。而使用查詢方法注入,則會每次得到一個新的實例,這實際上是因為我們使用factory.getBean("abstractLookupBean")獲得abstractBean時,Spring根據(jù)lookup-method的配置,覆寫了查詢方法,從而返回一個新的bean實例,正如前面提到的,這是通過CGLIB包來實現(xiàn)的。
????? 值得注意的是,我們將查詢方法定義為抽象的,這不是必須的,但這么做能防止我們忘記了配置lookup-method,從而使用了空的(當(dāng)然不是我們期望的)查詢方法。
????? 好了,讓我們再來看看方法替換,雖然它被劃分為方法注入的一種,但它和我們以前接觸到的注入方式有著很大的差異,以前我們都是注入一個bean,而方法替換則是替換bean中的方法實現(xiàn)。同查詢方法注入一樣,我們還是通過一個例子來理解方法替換的使用。
//The ReplacementTarget Class
public class
ReplacementTarget {
??? public String formatMessage(String msg) {
??????? return "<h1>" + msg + "</h1>";
??? }
??? public String formatMessage(Object msg) {
??????? return "<h1>" + msg + "</h1>";
??? }
}
????
現(xiàn)在由于某些原因,我們需要修改以String為參數(shù)的方法的實現(xiàn)方式,并且不能修改ReplacementTarget類的源代碼,借助于Spring的方法替換,我們可以輕松地實現(xiàn)。
???? 首先,創(chuàng)建一個實現(xiàn)了MethodReplacer接口的類。
public class FormatMessageReplacer implements MethodReplacer {
??? public Object reimplement(Object target, Method method, Object[] args)
throws Throwable {
??????? ...
??????? String msg = (String) args[0];
??????? return "<h2>" + msg + "</h2>";
??????? ...
??? }
}
<beans>
??? <bean id="methodReplacer"
????class="com.apress.prospring.ch5.mi.FormatMessageReplacer"/>
????<bean id="replacementTarget"
??? class="com.apress.prospring.ch5.mi.ReplacementTarget">
??????? <replaced-method name="formatMessage" replacer="methodReplacer">
??????????? <arg-type>String</arg-type>
??????? </replaced-method>
??? </bean>
??? <bean id="standardTarget"
??? class="com.apress.prospring.ch5.mi.ReplacementTarget"/>
</beans>
?????
通過replaced-method標(biāo)簽來配置需要替換的方法,name屬性指明了需要替換的方法名,replacer屬性指明了用哪個bean來實現(xiàn)替換操作。arg-type是用來指明需要替換的方法的參數(shù)類型的,在bean中有重載方法時(像ReplacementTarget類一樣),指明參數(shù)類型可以讓Spring明確知道需要替換的是哪個方法。值得注意的是,arg-type是模式匹配的,因此,String將匹配String和StringBuffer。
????? 方法替換在很多場合都是有用的,比如我們只想為一個特殊的bean改變它所依賴的另一個bean的實現(xiàn)方法,而不影響其它bean的引用。最好是為每個需要替換的方法,或者是一組重載的方法定義一個替換類,而不要為眾多毫不相關(guān)的方法使用同一個替換類。這可以減少很多沒必要的比較,從而提高代碼的執(zhí)行效率。