Spring 之方法注入

在大多数应用场景中,容器中的大多数beans是singleton的。当一个singleton bean需要与另一个singleton bean合作时,或者一个非singleton bean与另一个非singleton bean合作时,一般处理这种依赖,是在另一个中定义一个property的bean。当这个bean的声明周期不同的时候问题就来了。假设singleton bean A需要使用非singleton bean B(property),可能在A的每个方法调用上。容器仅仅创建singleton  bean A 一次,并且只有一次机会来设置其属性。容器不能每次需要bean B的时候提供给bean A一个bean B的实例。



一种解决方案是放弃某些控制反转。你可以实现ApplicationContextAware接口使得容器的bean A意识到,并且通过调用getBean使得容器在每次bean A需要的时候请求bean B的实例。例子如下:


// a class that uses a stateful Command-style class to perform some processing
package fiona.apple;

// Spring-API imports
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) {
        // grab a new instance of the appropriate Command
        Command command = createCommand();
        // set the state on the (hopefully brand new) Command instance
        command.setState(commandState);
        return command.execute();
    }

    protected Command createCommand() {
        // notice the Spring API dependency!
        return this.applicationContext.getBean("command", Command.class);
    }

    public void setApplicationContext(
            ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}


这种处理方式不建议使用,因为业务代码与Spring框架耦合了。方法注入,Spring IoC容器的某点高级功能,允许在一个干净的方式中使用这种情况。


lockup method injection(锁死方法注入)


锁死方法注入是容器重写所管理beans方法的能力,返回容器中另一个方法bean的锁死结果。锁死在前面提到的章节中一般地涉及到一个property bean。Spring框架通过使用来自CGLB包的字节码产生器实现这个方法注入来产生动态的子类,重写这个方法。


注意:为使这个动态子类工作,其父类不能是final类型,并且需重写的方法也不能是fianl。如果父类中有abstract类型的方法也需要你自己实现。最后,已经是方法注入的目标的对象不能被序列化。在Spring3.2中,不再将CGLB添加到你的系统路径中,因为CGLB已经打包在Spring核心包的org.springframe内。这样做也是为了避免其他使用CGLB版本的潜在冲突。


查看前面提到的CommandManager代码片段,发现Spring容器动态重写了createCommand()方法的实现。你的commandManager类将没有一些Spring依赖,这可以在改造的例子中看到。


package fiona.apple;

// no more Spring imports!

public abstract class CommandManager {

    public Object process(Object commandState) {
        // grab a new instance of the appropriate Command interface
        Command command = createCommand();
        // set the state on the (hopefully brand new) Command instance
        command.setState(commandState);
        return command.execute();
    }

    // okay... but where is the implementation of this method?
    protected abstract Command createCommand();
}

在包含了要注入方法的客户端类(CommandManager),要注入的方法需要如下的标签格式:

public|protected> [abstract] <return-type> theMethodName(no-arguments);

如果这个方法是抽象类型的,动态创建的子类需要实现这个方法。否则,动态创建的子类重写原始类的这个具体方法。例如:

<!-- a stateful bean deployed as a prototype (non-singleton) -->
<bean id="command" class="fiona.apple.AsyncCommand" scope="prototype">
    <!-- inject dependencies here as required -->
</bean>

<!-- commandProcessor uses statefulCommandHelper -->
<bean id="commandManager" class="fiona.apple.CommandManager">
    <lookup-method name="createCommand" bean="command"/>
</bean>


无论什么时候 commandManager需要一个command实例时,其调用自己的createCommand方法。你必须部署这个command bean为prototype,如果确实是这样需要的。如果部署为singleton,每次返回时都是command bean的同一个实例。


Arbitrary method replacement(任意方法替代)


相较于lookup 方法注入好一点的方法注入是可以用另一个方法实现替代管理bean的任意方法。


用基于XML配置的元数据,可以使用replaced-method元素用另一个方法替换一个已经存在的方法实现,这针对一个部署的bean。考虑下面的类,有computeValue方法,需要我们重写:

public class MyValueCalculator {

    public String computeValue(String input) {
        // some real code...
    }

    // some other methods...

}

实现了org.springframework.beans.factory.support.MethodReplacer接口的类提供了新方法的定义。


/**
 * meant to be used to override the existing computeValue(String)
 * implementation in MyValueCalculator
 */
public class ReplacementComputeValue implements MethodReplacer {

    public Object reimplement(Object o, Method m, Object[] args) throws Throwable {
        // get the input value, work with it, and return a computed result
        String input = (String) args[0];
        ...
        return ...;
    }
}

这个bean定义部署在原始类中,并制定了需要重写的方法:

<bean id="myValueCalculator" class="x.y.z.MyValueCalculator">
    <!-- arbitrary method replacement -->
    <replaced-method name="computeValue" replacer="replacementComputeValue">
        <arg-type>String</arg-type>
    </replaced-method>
</bean>

<bean id="replacementComputeValue" class="a.b.c.ReplacementComputeValue"/>


你可以使用<arg-type/>元素来指定需要重写的方法签名。仅当重载了这个方法并且类中有多个变量,参数签名就很必要了。为了方便,String类型可能是String权限定名的子字符串。例如:

java.lang.String

String

Str


因为参数数量通常足以区分开来。这种简写可以大量的输入,允许你输入最短的字符串来匹配类型。



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值