阅读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数据类型有哪些