方法注入(Method injection)
方法注入(Method injection)
Spring提供兩種機製去注入方法,分別是 Lookup method inject,Arbitrary method replacement。Lookup method inject隻提供返回值注入,Arbitrary method replacement可以替換任意方法來達到注入。
Lookup method inject
有時我們需要在一個bean A中調用另一個bean B的方法,通常我們會添加一個字段,然後使用依賴注入把bean B的實例注入到這個字段上。這種情況下在bean A 和 bean B都是singleton時沒問題,但是在 bean A是singleton和bean B是非singleton時就可能出現問題。因為bean B為非singleton , 那麼bean B是希望他的使用者在一些情況下創建一個新實例,而bean A使用字段把bean B的一個實例緩存了下來,每次都使用的是同一個實例。
我們假設bean B是一個prototype
一種解決辦法是不使用字段依賴注入,每次使用bean B的時候都去bean容器中重新獲取
/**
* 這是一個命令執行類,提供一個process方法,執行用戶的命令
*/
public class CommandManager{
//使用依賴注入,把applicationContext注入進來
@Autowire
private ApplicationContext applicationContext;
/**
* 根據用戶指定的參數,每次使用一個新的Command實例去執行命令
* @param commandState 執行的參數
* @return 執行後的返回值
*/
public Object process(Map commandState) {
// 每次使用都調用createCommand去獲取一個新實例
Command command = createCommand();
command.setState(commandState);
return command.execute();
}
//從applicationContext獲取一個新的Command實例
protected Command createCommand() {
return this.applicationContext.getBean("command", Command.class);
}
}
上麵的代碼是每次都從applicationContext重新獲取一個新實例來實現的。Spring提供了一個Lookup method inject機製,它可以改變方法的返回值,來達到方法注入的效果。對應的有annotation和xml兩種使用方式。
annotation的使用方式@Lookup,把@Lookup加到你要改變方法返回值的方法上
public abstract class CommandManager{
public Object process(Map commandState) {
// 每次使用都調用createCommand去獲取一個新實例
Command command = createCommand();
command.setState(commandState);
return command.execute();
}
/**
* Loopup的注釋中的寫明了需要返回的bean名字,如果沒有寫bean name,那麼會根據createCommand的函數返回值類型去查找對應的bean
* @return
*/
@Lookup("command")
protected abstract Command createCommand();
}
Spring的Lookup method inject實現原理的是使用CGLIB動態生成一個類去繼承CommandManager
,重寫createCommand
方法。然後根據@Lookup中指定的bean Name或者createCommand
方法的返回類型判斷需要返回的bean。createCommand
可以是abstract和可以不是。因為使用的是繼承,所以CommandManager
類和createCommand
方法都不能是final的。
createCommand
方法的簽名需要滿足如下要求
<public|protected> [abstract] <return-type> theMethodName(no-arguments);
對應實現的XML配置
<bean scope="prototype">
</bean>
<bean >
<lookup-method name="createCommand" bean="command"/>
</bean>
訪問不同Scope的bean,可以使用ObjectFactory
/ Provider
注入,詳情查看Scoped beans as dependencies.
Arbitrary method replacement
Lookup method inject隻是改變了方法的返回值,但是method replacement可以替換bean 容器裏任意方法的實現,達到方法的完全注入,一般情況下不要這個使用特性!
此特性,隻能基於XML配置實現。假如我們要替換如下類的computeValue
方法
public class MyValueCalculator {
public String computeValue(String input) {
//...真實代碼
}
}
第一步,我們要現實org.springframework.beans.factory.support.MethodReplacer
接口
public class ReplacementComputeValue implements MethodReplacer {
/**
* 當我們替換的方法被調用時,容器就會代理到這裏,在這裏執行我們要替換的執行邏輯
* @param o 替換方法執行時對應的實例
* @param m 替換方法
* @param args 替換方法執行時傳入的參數
* @return
* @throws Throwable
*/
public Object reimplement(Object o, Method m, Object[] args) throws Throwable {
String input = (String) args[0];
...
return ...;
}
}
第二步,在XML中,使用replaced-method
元素進行配置.
<bean >
<!-- 需要替換的方法 -->
<replaced-method name="computeValue" replacer="replacementComputeValue">
<arg-type>String</arg-type>
</replaced-method>
</bean>
<bean />
在上麵的xml中,在元素replaced-method
中使用了arg-type
。它的作用是在有多個方法重載時,根據arg-type
中指定的參數class名字來確定具體替換哪一個方法。arg-type
中的值可以是類全路徑的一個子串,如下麵所有的值都可以匹配java.lang.String
java.lang.String
String
Str
詳情查看文檔地址:https://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/
最後更新:2017-04-18 15:30:26