閱讀502 返回首頁    go 阿裏雲 go 技術社區[雲棲]


方法注入(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

  上一篇:go js運算符複習
  下一篇:go js數據類型有哪些