《Spring Framework实战》15:4.1.4.6.方法注入

news2025/3/1 3:26:46
欢迎观看《Spring Framework实战》视频教程

        1. 方法注入

在大多数应用场景中,容器中的大多数bean都是单例(singletons)的。当单例bean需要与另一个单例bean协作或非单例bean需与另一非单例bean协作时,通常通过将一个bean定义为另一个bean的属性来处理依赖关系。当bean的生命周期不同时,就会出现问题。假设单例bean A需要使用非单例(prototype)bean B,可能是在A的每次方法调用时。容器只创建单例bean A一次,因此只有一次机会设置属性。容器不能在每次需要bean B时为bean A提供bean B的新实例。

一个解决方案是放弃一些控制反转。您可以通过实现ApplicationContextAware接口,并在每次bean A需要时对容器进行getBean(“B”)调用,请求(通常是新的)bean B实例,使bean A知道容器。

以下示例显示了这种方法:

Java

package org.examples;

import java.util.Map;

public class Command {

    private Map state;

    public Map getState() {
        return state;
    }

    public void setState(Map state) {
        this.state = state;
    }

    public String execute() {
        return "Command " + this + " execute, state = " + this.state;
    }

}

package org.examples;

import java.util.Map;

/**
 * 当bean的生命周期不同时,就会出现问题。
 */
public class CommandManager {

    public Command command;

    public Command getCommand() {
        return command;
    }

    public void setCommand(Command command) {
        this.command = command;
    }

    public String process(Map State) {
        // 在(希望是全新的)Command实例上设置状态
        this.command.setState(State);
        return this.command.execute();
    }

}

<bean id="commandManager" class="org.examples.CommandManager" scope="singleton">
    <!-- collaborators and configuration for this bean go here -->
    <property name="command" ref="command"/>
</bean>

<bean id="command" class="org.examples.Command" scope="prototype">
    <!-- collaborators and configuration for this bean go here -->
    <property name="state">
        <map>
            <entry key="state1" value="aaa"/>
            <entry key="state2" value="bbb"/>
            <entry key="state3" value="ccc"/>
        </map>
    </property>
</bean>

CommandManager commandManager = (CommandManager) context.getBean(CommandManager.class);
Map map1 = new HashMap();
map1.put("state", "state111");
System.out.println(commandManager.process(map1));
Map map2 = new HashMap();
map2.put("state", "state222");
System.out.println(commandManager.process(map2));

package org.examples;

// Spring-API imports

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

import java.util.Map;

/**
 * A class that uses a stateful Command-style class to perform
 * some processing.
 * <p>
 * 您可以通过实现ApplicationContextAware接口,
 * 并在每次bean A需要时对容器进行getBean(“B”)调用,
 * 请求(通常是新的)bean B实例,使bean A知道容器。
 */
public class CommandManager implements ApplicationContextAware {

    private ApplicationContext applicationContext;

    public String 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实例上设置状态
        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 id="commandManager" class="org.examples.CommandManager" scope="singleton">
    <!-- collaborators and configuration for this bean go here -->
</bean>

<bean id="command" class="org.examples.Command" scope="prototype">
    <!-- collaborators and configuration for this bean go here -->
    <property name="state">
        <map>
            <entry key="state1" value="aaa"/>
            <entry key="state2" value="bbb"/>
            <entry key="state3" value="ccc"/>
        </map>
    </property>
</bean>

前面的内容是不可取的,因为业务代码意识到并耦合到Spring Framework。方法注入是Spring IoC容器的一个稍微高级的功能,可以让你干净利落地处理这个用例。

你可以在这篇博客文章中关于方法注入的动机。

          1. 查找方法注入

查找方法注入是容器覆盖容器管理bean上的方法并返回容器中另一个命名bean的查找结果的能力。查找通常涉及一个原型bean,如前一节所述的场景。Spring Framework通过使用CGLIB库中的字节码生成来动态生成覆盖该方法的子类,从而实现了这种方法注入。

  1. 为了使这种动态子类工作,Spring bean容器子类的类不能是final的,要重写的方法也不能是final的。
  2. 对具有抽象方法的类进行单元测试需要您自己对类进行子类化,并提供抽象方法的存根实现。
  3. 组件扫描也需要具体的方法,这需要具体的类来拾取。
  4. 另一个关键限制是查找方法不适用于工厂方法,特别是不适用于配置类中的@Bean方法,因为在这种情况下,容器不负责创建实例,因此无法动态创建运行时生成的子类。

在前面代码片段中的CommandManager类的情况下,Spring容器动态覆盖createCommand()方法的实现。CommandManager类没有任何Spring依赖项,如重新编写的示例所示:

Java

package org.examples;

// 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="myCommand" class="org.examples.AsyncCommand" scope="prototype">

<!-- inject dependencies here as required -->

</bean>

<!-- commandManager uses myCommand prototype bean -->

<bean id="commandManager" class="org.examples.CommandManager">

<lookup-method name="createCommand" bean="myCommand"/>

</bean>

标识为commandManager的bean在需要myCommand bean的新实例时调用自己的createCommand()方法。如果确实需要,您必须小心地将myCommand bean部署为原型。如果它是单例,则每次都会返回相同的myCommand bean实例。

或者,在基于注释的组件模型中,您可以通过@lookup注释声明查找方法,如下例所示:

Java

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根据查找方法的声明返回类型进行解析:

Java

public abstract class CommandManager {

public Object process(Object commandState) {

Command command = createCommand();

command.setState(commandState);

return command.execute();

}

@Lookup

protected abstract Command createCommand();

}

请注意,您通常应该使用具体的存根实现声明此类带注释的查找方法,以便它们与Spring的组件扫描规则兼容,默认情况下抽象类会被忽略。此限制不适用于显式注册或显式导入的bean类。

访问不同作用域的目标bean的另一种方法是ObjectFactory/Provider注入点。请参阅作为依赖关系的作用域Bean。

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

          1. 任意方法替换

一种不如查找方法注入有用的方法注入形式是能够用另一种方法实现替换托管bean中的任意方法。您可以安全地跳过本节的其余部分,直到您真正需要此功能。

使用基于XML的配置元数据,您可以使用替换的方法元素将已部署bean的现有方法实现替换为另一个方法实现。考虑以下类,它有一个我们想要重写的名为computeValue的方法:

Java

public class MyValueCalculator {

public String computeValue(String input) {

// some real code...

}

// some other methods...

}

一个实现org.springframework.beans.factory.support的类。MethodReplacer接口提供了新的方法定义,如下例所示:

Java

/**

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

您可以在<replaced method/>元素中使用一个或多个<arg type/>元素来指示被重写方法的方法签名。只有当方法重载并且类中存在多个变量时,才需要参数的签名。为方便起见,参数的类型字符串可以是完全限定类型名称的子字符串。例如,以下所有匹配项

java.lang.String :

java.lang.String

String

Str

因为参数的数量通常足以区分每种可能的选择,所以这个快捷方式可以节省大量的输入,只让你输入与参数类型匹配的最短字符串。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2274739.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Flutter:使用FVM安装多个Flutter SDK 版本和使用教程

一、FVM简介 FVM全称&#xff1a;Flutter Version Management FVM通过引用每个项目使用的Flutter SDK版本来帮助实现一致的应用程序构建。它还允许您安装多个Flutter版本&#xff0c;以快速验证和测试您的应用程序即将发布的Flutter版本&#xff0c;而无需每次等待Flutter安装。…

目标客户营销(ABM)结合开源AI智能名片2+1链动模式S2B2C商城小程序的策略与实践

摘要&#xff1a;在数字化营销日益盛行的今天&#xff0c;目标客户营销&#xff08;Account Based Marketing, ABM&#xff09;作为一种高度定制化的营销策略&#xff0c;正逐步成为企业获取高质量客户、提升市场竞争力的重要手段。与此同时&#xff0c;开源AI智能名片21链动模…

docker(目录挂载、卷映射)

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、目录挂载1.命令2.案例3.补充 二、卷映射1.命令2.案例 总结 前言 在使用docker部署时&#xff0c;我们如果要改变一些配置项目&#xff0c;不可能每次都进入…

opencv warpAffine仿射变换C++源码分析

基于opencv 3.1.0源代码 sources\modules\imgproc\src\imgwarp.cpp void cv::warpAffine( InputArray _src, OutputArray _dst,InputArray _M0, Size dsize,int flags, int borderType, const Scalar& borderValue ) {...if( !(flags & WARP_INVERSE_MAP) ){//变换矩阵…

使用 IntelliJ IDEA 创建简单的 Java Web 项目

以下是使用 IntelliJ IDEA 创建几个简单的 Java Web 项目的步骤&#xff0c;每个项目实现基本的登录、注册和查看列表功能&#xff0c;依赖 Servlet/JSP 和基本的 Java Web 开发。 前置准备 确保安装了 IntelliJ IDEA Ultimate&#xff08;社区版不支持 Web 应用&#xff09;。…

R语言在森林生态研究中的魔法:结构、功能与稳定性分析——发现数据背后的生态故事!

森林生态系统结构、功能与稳定性分析与可视化研究具有多方面的重要意义&#xff0c;具体如下&#xff1a; 一、理论意义 ●深化生态学理论 通过研究森林生态系统的结构、功能与稳定性&#xff0c;可以深化对生态系统基本理论的理解。例如&#xff0c;生物多样性与生态系统稳定性…

QML states和transitions的使用

一、介绍 1、states Qml states是指在Qml中定义的一组状态&#xff08;States&#xff09;&#xff0c;用于管理UI元素的状态转换和属性变化。每个状态都包含一组属性值的集合&#xff0c;并且可以在不同的状态间进行切换。 通过定义不同的状态&#xff0c;可以在不同的应用场…

Git:Cherry-Pick 的使用场景及使用流程

前面我们说了 Git合并、解决冲突、强行回退等解决方案 >> 点击查看 这里再说一下 Cherry-Pick功能&#xff0c;Cherry-Pick不是merge&#xff0c;只是把部分功能代码Cherry-Pick到远程的目标分支 git cherry-pick功能简介&#xff1a; git cherry-pick 是用来从一个分…

【SpringAOP】Spring AOP 底层逻辑:切点表达式与原理简明阐述

前言 &#x1f31f;&#x1f31f;本期讲解关于spring aop的切面表达式和自身实现原理介绍~~~ &#x1f308;感兴趣的小伙伴看一看小编主页&#xff1a;GGBondlctrl-CSDN博客 &#x1f525; 你的点赞就是小编不断更新的最大动力 &am…

python基础和redis

1. Map函数 2. filter函数 numbers generate_numbers() filtered_numbers filter(lambda x: x % 2 0, numbers) for _ in range(5):print(next(filtered_numbers)) # 输出: 0 2 4 6 83. filter map 和 reduce 4. picking and unpicking 5. python 没有函数的重载&#xff0…

python-42-使用selenium-wire爬取微信公众号下的所有文章列表

文章目录 1 seleniumwire1.1 selenium-wire简介1.2 获取请求和响应信息2 操作2.1 自动获取token和cookie和agent2.3 获取所有清单3 异常解决3.1 请求url失败的问题3.2 访问链接不安全的问题4 参考附录1 seleniumwire Selenium WebDriver本身并不直接提供获取HTTP请求头(header…

Windows安装ES单机版设置密码

下载ES ES下载链接 我用的是7.17.26 启动前配置 解压之后打开D:\software\elasticsearch-7.17.26\bin\elasticsearch-env.bat 在elasticsearch-env.bat文件中修改jdk的路径 修改前 修改内容 if defined ES_JAVA_HOME (set JAVA"D:\software\elasticsearch-7.17.26\…

Java并发编程面试题:内存模型(6题)

&#x1f9d1; 博主简介&#xff1a;CSDN博客专家&#xff0c;历代文学网&#xff08;PC端可以访问&#xff1a;https://literature.sinhy.com/#/?__c1000&#xff0c;移动端可微信小程序搜索“历代文学”&#xff09;总架构师&#xff0c;15年工作经验&#xff0c;精通Java编…

标准应用 | 2025年网络安全服务成本度量实施参考

01 网络安全服务成本度量依据相关新变化 为了解决我国网络安全服务产业发展中面临的服务供需两方对于服务成本组成认知偏差较大、网络安全服务成本度量缺乏依据的问题&#xff0c;中国网络安全产业联盟&#xff08;CCIA&#xff09;组织北京赛西科技发展有限责任公司、北京安…

太速科技-FMC141-四路 250Msps 16bits AD FMC子卡

FMC141-四路 250Msps 16bits AD FMC子卡 一、产品概述&#xff1a; 本板卡基于 FMC 标准板卡&#xff0c;实现 4 路 16-bit/250Msps ADC 功能。遵循 VITA 57 标准&#xff0c;板卡可以直接与xilinx公司或者本公司 FPGA 载板连接使用。板卡 ADC 器件采用 ADI 公司 AD9467 芯…

通义灵码在跨领域应用拓展之物联网篇

目录 一.引言 二.通义灵码简介 三.通义灵码在物联网领域的设备端应用 1.传感器数据采集 (1).不同类型传感器的数据读取 (2).数据转换与预处理 2.设备控制指令接收和执行 (1).指令解析与处理 (2).设备动作执行 四.通义灵码在物联网领域的云端平台应用 1.数据存储和管…

使用Kubernetes部署Spring Boot项目

目录 前提条件 新建Spring Boot项目并编写一个接口 新建Maven工程 导入 Spring Boot 相关的依赖 启动项目 编写Controller 测试接口 构建镜像 打jar包 新建Dockerfile文件 Linux目录准备 上传Dockerfile和target目录到Linux 制作镜像 查看镜像 测试镜像 上传镜…

C#基础之 继承类相关构造函数使用

类构造函数 作用是为 类中成员变量进行赋值操作 单个类的时候 一般不会有什么思路问题&#xff0c;主要说明一下 有继承关系类的时候 当存在继承关系的类 如 A&#xff1a;B 首先要注意第一点&#xff1a;顺序 那么在构造函数时 顺序是由 B先构造 然后 A在构造 注意第二点方法…

【leetcode刷题】:双指针篇(有效三角形的个数、和为s的两个数)

文章目录 一、有效三角形的个数题目解析算法原理代码编写 二、和为s的两个数题目解析算法原理代码编写 一、有效三角形的个数 题目解析 有效三角形的个数【点击跳转】 题目意思很好理解&#xff1a;就是在一堆非负整数的数组里&#xff0c;随机选三个数进行搭配&#xff0c;…

【Unity3D】apk加密(global-metadata.dat加密)

涉及&#xff1a;apk、aab、global-metadata.dat、jks密钥文件、APKTool、zipalign 使用7z打开apk文件观察发现有如下3个针对加密的文件。 xxx.apk\assets\bin\Data\Managed\Metadata\global-metadata.dat xxx.apk\lib\armeabi-v7a\libil2cpp.so xxx.apk\lib\arm64-v8a\libil…