方法注入(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 id="command" class="AsyncCommand" scope="prototype">
</bean>

<bean id="commandManager" class="CommandManager">
    <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 id="myValueCalculator" class="x.y.z.MyValueCalculator">
    <!-- 需要替换的方法 -->
    <replaced-method name="computeValue" replacer="replacementComputeValue">
        <arg-type>String</arg-type>
    </replaced-method>
</bean>

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

在上面的xml中,在元素replaced-method中使用了arg-type。它的作用是在有多个方法重载时,根据arg-type中指定的参数class名字来确定具体替换哪一个方法。arg-type中的值可以是类全路径的一个子串,如下面所有的值都可以匹配java.lang.String

java.lang.String
String
Str

详情查看文档地址:http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#beans-factory-method-injection

时间: 2024-08-23 05:48:56

方法注入(Method injection)的相关文章

Spring中单例bean访问非单例bean的第一种方式:方法注入

方法注入在Spring中是很少用的,主要应用是, 对象中可能定义了一个受保 护的抽象方法,而容器可能在运行时实现他以返回由容器查询得到的对象. 方法注入的最好用途之一就是处理单态.无状态对象需要调用非单态.有状 态或者非线程安全对象的情况. 以前刚接触Spring时,如果在单例bean中调用非单例bean,只要把那个非单 例bean 的singleton设置为false就可以了.其实不然,大家想,我们创建了一 个单例对象,在此单例对象中所用到的其它bean也只会创建一次--(大多数情 况是这样的

(方法调配)Method Swizzling

一.概念 方法调配:因为Objective-C是运行时语言,也就是说究竟会调用何种方法要在运行期才能解析出来.那么我们其实也可以在运行时改变选择子名称.这样我们既不需要查看到源代码,又没有必要去重写子类来覆写方法就能改变类本身的功能.这样一来新功能就会在类的所有实例中表现出来,而不仅限于那些重写子类的实例.这种方案就叫做"方法调配"(method swizzling). IMP:类方法列表会把选择子的名称映射到方法的实现上,使得动态消息派发系统能够据次找到应该调用的方法,这些方法均以指

ASP.NET MVC 5 - 验证编辑方法(Edit method)和编辑视图(Edit view)

原文:ASP.NET MVC 5 - 验证编辑方法(Edit method)和编辑视图(Edit view) 在本节中,您将验证电影控制器生成的编辑方法(Edit action methods)和视图.但是首先将修改点代码,使得发布日期属性(ReleaseDate)看上去更好.打开Models \ Movie.cs文件,并添加高亮行如下所示: using System; using System.ComponentModel.DataAnnotations; using System.Data.

java反射机制如何 得到当前方法的 method对象

问题描述 写了个获取调用方法的method对象的方法,但是无法区分重载方法publicstaticMethodgetCurrentMethod(){StackTraceElement[]yste=Thread.currentThread().getStackTrace();if(yste.length<2){returnnull;}/**getMethodName**/Stringstr="";for(inti=0;i<yste.length;i++){if(yste[i]

工厂模式之二 - 工厂方法(Factory Method)

工厂方法(Factory Method)模式又叫做多态性工厂(Polymorphic Factory). 简单工厂模式的优缺点 优点:将类的创建逻辑从客户端移入工厂类. 缺点:对开-闭原则支持不够,如果有新类加入,必须修改工厂类的逻辑.   工厂方法克服了简单工厂模式的缺点,引入了多态性.   工厂方法和简单工厂的区别: 简单工厂模式的核心是一个具体类,工厂方法模式的核心是一个抽象类.  下面我们来看一下工厂方法的具体实现,我们还是以画图程序为例子. 图形的代码结构和简单工厂类似,读者可以参看简

Unity Application Block 1.0系列(4):方法调用注入(Method Call Injection)

什么情况下使用Method CallInjection 当实例化父对象时也能自动实例化所依赖的对象 通过简单的方式使得很容易做到在代码中查看每个类所依赖的项 父对象有很多相互之间有关联关系的构造器,导致在调试和维护时很不方便 父对象包含有很多参数构造器,特别是参数类型相似的只能通过参数的位置来辨别的 隐藏依赖的对象,不作为属性暴露出去 通过修改依赖对象的代码来控制哪些对象可以被注入,而不用改动父对象或应用程序 准备工作 public interface IPlayer { void Play()

Unity Application Block 1.0系列(2):构造子注入(Constructor Injection)

什么情况下使用Constructor Injection 当实例化父对象时也能自动实例化所依赖的对象 通过简单的方式使得很容易做到在代码中查看每个类所依赖的项 父对象的构造器不需要很多相互间有关联关系的构造器 父对象的构造器不需要很多参数 通过不使用属性和方法暴露给调用程序,达到封装字段值以不能被看到的目的 通过修改依赖对象的代码来控制哪些对象可以被注入,而不用改动父对象或应用程序 准备工作 public class Song { public string Singer { get { ret

Unity Application Block 1.0系列(3):属性/设值方法注入(Property/Setter Inj

什么情况下使用Property (Setter) Injection 当实例化父对象时也能自动实例化所依赖的对象 通过简单的方式使得很容易做到在代码中查看每个类所依赖的项 父对象有很多相互之间有关联关系的构造器,导致在调试和维护时很不方便. 父对象包含有很多参数构造器,特别是参数类型相似的只能通过参数的位置来辨别的 让用户(将调用这些代码的程序)更方便的看到有哪些对象可以用,这在Constructor Injection里是没办法实现的. 通过修改依赖对象的代码来控制哪些对象可以被注入,而不用改

攻击方式学习之SQL注入(SQL Injection)第1/3页_安全相关

这就给不怀好意的同学可乘之机,利用输入一些奇特的查询字符串,拼接成特定的SQL语 句,即可达到注入的目的.不仅可以获取数据库重要信息,权限没有设置好的话甚至可以删除掉整个表.因此,SQL注入漏洞还是相当的严重的.发现以前偶刚学 写的网站的时候也是靠拼接SQL语句吃饭滴-- 示例 为了更好了学习和了解SQL注入的方法,做了一个示例网页,界面如下:  点击登陆这块的代码如下,注意第5行,我们使用了拼接SQL语句: 复制代码 代码如下: private void Login() { string un