方法注入(Method injection)

简介: 方法注入,重写方法,替换方法!@Lookup,lookup-method,replaced-method!Lookup method inject!Arbitrary method replacement!

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

目录
相关文章
|
数据采集 存储 数据库连接
Requests与BeautifulSoup:高效解析网页并下载资源
Requests与BeautifulSoup:高效解析网页并下载资源
|
人工智能 自然语言处理 搜索推荐
如何构建一套qwen-max智能体拥有媲美通义千问在线接口的能力
智能系统通过任务识别、决策引擎、工具选择和结果整合,自动选择合适的工具和方法,高效处理查询、生成、翻译、图像处理等任务,提供精准的解决方案。系统支持自然语言理解、任务分类、语义解析与意图识别,确保任务的准确执行和反馈。
470 3
|
Java 调度
ScheduledExecutorService:多线程任务调度
ScheduledExecutorService:多线程任务调度
ScheduledExecutorService:多线程任务调度
|
Java 调度
ScheduledExecutorService使用介绍
JUC包(java.util.concurrent)中提供了对定时任务的支持,即ScheduledExecutorService接口。 本文对ScheduledExecutorService的介绍,将基于Timer类使用介绍进行,因此请先阅读Timer类使用介绍文章。
1923 1
|
关系型数据库 MySQL 大数据
MySQL分区与分表:优化性能与提升可扩展性
本文深入探讨了MySQL数据库中的分区与分表策略,通过详细的代码示例,解释了分区的概念与用途、不同的分区类型以及创建分区表的步骤。同时,文章还介绍了分表的概念、策略和实际操作方法,以代码演示展示了如何创建分表、插入数据以及查询数据。分区和分表作为优化数据库性能和提升可扩展性的关键手段,通过本文的阐述,读者将能够深入了解如何根据数据特点选择合适的分区方式,以及如何灵活地处理大量数据,提高查询和维护效率。这些技术将为数据库设计和优化提供有力支持,确保在大数据场景下能够高效地管理和查询数据。
2849 0
|
消息中间件 Kafka RocketMQ
Kafka重平衡机制
当集群中有新成员加入,或者某些主题增加了分区之后,消费者是怎么进行重新分配分区再进行消费的?这里就涉及到重平衡(Rebalance)的概念,下面我就给大家讲解一下什么是 Kafka 重平衡机制,我尽量做到图文并茂通俗易懂。
2188 0
Kafka重平衡机制
|
JSON Java API
Java一分钟之-JPA实体关系:一对一, 一对多, 多对多
【6月更文挑战第14天】Java Persistence API (JPA) 的 ORM 规范简化了数据库操作,重点是实体关系映射。本文讨论了三种主要关系:一对一、一对多和多对多。对于每种关系,都指出了常见问题(如循环引用、懒加载异常)和避免策略(使用注解解决循环引用,明确级联操作边界等)。同时,提供了示例代码以展示如何在实践中设置这些关系。理解并妥善处理这些问题能提升开发效率和数据准确性。
915 1
|
开发框架 小程序 前端开发
Remax
Remax 是一款基于 Vue.js 的微信小程序开发框架,它提供了一套简洁、完整的 API,让开发者能够快速、高效地开发出功能丰富、性能优良的微信小程序。
693 1
|
Java Maven Spring
SpringBoot运行出现 Lookup method resolution failed; nested exception is java.lang.IllegalStateException
SpringBoot运行出现 Lookup method resolution failed; nested exception is java.lang.IllegalStateException
4141 0
|
缓存 安全 Java
java-- 字符串+拼接详解, 性能调优 (底层原理实现)
java-- 字符串+拼接详解, 性能调优 (底层原理实现)
427 0