Spring 方法注入

方法注入

        在大多数的应用场景下,多数的bean都是单例的。当这个单例的bean需要和另一个单例的或者非单例的bean协作使用的时候,开发者只需要配置依赖bean为这个bean的属性即可。 但是有时会因为bean具有不同的生命周期而产生问题。假设单例的bean A在每个方法调用中使用了非单例的bean B。容器只会创建bean A一次,而只有一个机会来配置属性。 那么容器就无法为每一次创建bean A时都提供新的bean B实例。

        一种解决方案就是放弃IoC,开发者可以通过实现ApplicationContextAware接口让bean A对ApplicationContext可见。 从而通过调用getBean("B")来在bean A 需要新的实例的时候来获取到新的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;
    }
}

查找方法注入

        查找方法注入是容器覆盖管理bean上的方法的能力,以便返回容器中另一个命名bean的查找结果。查找方法通常涉及原型bean。 Spring框架通过使用CGLIB库生成的字节码来生成动态子类重写的方法实现此注入。

        如果想让这个动态子类正常工作,那么Spring容器所继承的Bean不能是final的,而覆盖的方法也不能是final的。

        对具有抽象方法的类进行单元测试时,需要开发者对类进行子类化,并提供抽象方法的具体实现。

        组件扫描也需要具体的方法,因为它需要获取具体的类。

        另一个关键限制是查找方法不适用于工厂方法,特别是在配置类中不使用@Bean的方法。因为在这种情况下,容器不负责创建实例,因此不能在运行时创建运行时生成的子类。

对于前面代码片段中的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);

如果方法是abstract的, 那么动态生成的子类会实现该方法。否则,动态生成的子类将覆盖原始类定义的具体方法。例如:

<!-- a stateful bean deployed as a prototype (non-singleton) -->
<bean id="myCommand" 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="myCommand"/>
</bean>

当需要新的myCommand bean实例时,标识为commandManager的bean会调用自身的createCommand()方法.开发者必须小心部署myCommand bean为原型bean. 如果所需的bean是单例的,那么每次都会返回相同的myCommand bean实例.

另外,如果是基于注解的配置模式,你可以在查找方法上定义@Lookup注解,如下:

public abstract class CommandManager {

    public Object process(Object commandState) {
        Command command = createCommand();
        command.setState(commandState);
        return command.execute();
    }

    @Lookup("myCommand")
    protected abstract Command createCommand();
}

或者,更常见的是,开发者也可以根据查找方法的返回类型来查找匹配的bean,如下

public abstract class CommandManager {

    public Object process(Object commandState) {
        MyCommand command = createCommand();
        command.setState(commandState);
        return command.execute();
    }

    @Lookup
    protected abstract MyCommand createCommand();
}

        注意开发者可以通过创建子类实现lookup方法,以便使它们与Spring的组件扫描规则兼容,同时抽象类会在默认情况下被忽略。这种限制不适用于显式注册bean或明确导入bean的情况。

        另一种可以访问不同生命周期的方法是ObjectFactory/Provider注入。

        您可能还会发现ServiceLocatorFactoryBean(在org.springframework.beans.factory.config包中)很有用。

替换任意方法

        从前面的描述中,我们知道查找方法是有能力来覆盖任何由容器管理的bean方法的。开发者最好跳过这一部分,除非一定需要用到这个功能。

        通过基于XML的元数据配置,开发者可以使用replaced-method元素来替换已存在方法的实现。考虑以下类,它有一个我们想要覆盖的名为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方法的XML配置如下类似于以下示例:

<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"/>

您可以在<replaced-method/>元素中使用一个或多个 元素来指示被覆盖的方法的方法。当需要覆盖的方法存在重载方法时,必须指定所需参数。 为了方便起见,字符串的类型会匹配以下类型,它完全等同于java.lang.String

java.lang.String
String
Str

因为,通常来说参数的个数已经足够区别不同的方法,这种快捷的写法可以省去很多的代码。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值