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

欢迎观看《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.mzph.cn/news/892609.shtml

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

相关文章

【ROS2】☆ launch之Python

☆重点 ROS1和ROS2其中一个很大区别之一就是launch的编写方式。在ROS1中采用xml格式编写launch&#xff0c;而ROS2保留了XML 格式launch&#xff0c;还另外引入了Python和YAML 编写方式。选择哪种编写取决于每位开发人员的爱好&#xff0c;但是ROS2官方推荐使用Python方式编写…

了解 Ansys Mechanical 中的网格方法:综合指南

网格是每个有限元分析 &#xff08;FEA&#xff09; 仿真的支柱。它将几何图形划分为离散单元&#xff0c;使 Ansys Mechanical 能够近似模型在各种条件下的行为。结构良好的网格可确保准确、可靠和计算高效的结果&#xff0c;而结构不佳的网格可能会导致错误、收敛问题或不必要…

学习threejs,使用TrackballControls相机控制器

&#x1f468;‍⚕️ 主页&#xff1a; gis分享者 &#x1f468;‍⚕️ 感谢各位大佬 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍⚕️ 收录于专栏&#xff1a;threejs gis工程师 文章目录 一、&#x1f340;前言1.1 ☘️THREE.TrackballControls 相…

云集电商:数据库的分布式升级实践|OceanBase案例

电商行业对数据库有哪些需求 云集电商作为一家传统电商企业&#xff0c;业务涵盖了美妆个护、服饰、水果生鲜、健康保健等多个领域&#xff0c;在创立四年后在纳斯达克上市&#xff08;股票代码&#xff1a;YJ&#xff09;。与京东、淘宝、拼多多等电商平台不同&#xff0c;云…

智能租赁系统提升效率与服务质量的全新解决方案

内容概要 智能租赁系统的崛起就像一场春雨&#xff0c;滋润着租赁行业的每一个角落。它通过先进的技术架构&#xff0c;结合数据管理&#xff0c;优化了以往繁琐的租赁流程&#xff0c;让整个过程如同顺畅的流水。比如&#xff0c;通过智能算法自动计算费用&#xff0c;使得用…

苹果手机(IOS系统)出现安全延迟进行中如何关闭?

苹果手机&#xff08;IOS系统&#xff09;出现安全延迟进行中如何关闭&#xff1f; 一、设置二、隐私与安全性三、失窃设备保护关闭 一、设置 二、隐私与安全性 三、失窃设备保护关闭

VxWorks [安装workbench之修改虚拟机Mac]

问题&#xff1a; 一、安装VMware 下载链接 [VMware 15 pro](https://segmentfault.com/a/1190000022562275)二、修改VMnet1的Mac ** 打开注册表 ** ctrl f 搜索VMnet1 增加字符串值 NetWorkAddress 00D6196C32 三、重启VMnet1 修改完成 四、重启 workbench

Redis十大数据类型详解

Redis&#xff08;一&#xff09; 十大数据类型 redis字符串&#xff08;String&#xff09; string是redis最基本的类型&#xff0c;一个key对应一个value string类型是二进制安全的&#xff0c;意思是redis的string可以包含任何数据。例如说是jpg图片或者序列化对象 一个re…

【从零开始使用系列】StyleGAN2:开源图像生成网络——环境搭建与基础使用篇(附大量测试图)

StyleGAN2 是英伟达团队 NVIDIA 提出的生成对抗网络&#xff08;GAN&#xff09;的一种改进版本。 它通过创新的网络架构&#xff0c;能够生成细节丰富、逼真的图像&#xff0c;特别在高频细节&#xff08;如皮肤纹理、光照等&#xff09;的表现上表现卓越。与传统 GAN 相比&am…

【三维数域】三维数据调度-负载均衡和资源优化

在处理大规模三维数据时&#xff0c;负载均衡和资源优化是确保系统高效运行、提供流畅用户体验的关键。这两者不仅影响到系统的性能和稳定性&#xff0c;还直接决定了用户交互的质量。以下是关于如何在三维数据调度中实现有效的负载均衡和资源优化的详细探讨。 一、负载均衡 负…

成功案例分享 — 芯科科技助力涂鸦智能打造Matter over Thread模块,简化Matter设备开发

芯科科技&#xff08;Silicon Labs&#xff09;的愿景之一是让开发者每天都能够更轻松地开发无线物联网&#xff08;IoT&#xff09;。特别是在拥有相同愿景的合作伙伴的帮助下&#xff0c;我们每天都在取得进步。但是要想弥合知识水平和物联网开发之间的差距仍会面临一定的挑战…

如何将 sqlserver 数据迁移到 mysql

文章目录 前言一、导出SQL Server 数据二、转换数据格式为MySQL兼容格式三、导入数据到MySQL数据库五、使用ETL工具六、通过 navicat 工具七、总结 前言 将 SQL Server 数据迁移到 MySQL 是一个常见的数据库迁移任务&#xff0c;通常涉及以下几个关键步骤&#xff1a;导出 SQL…

10Hive性能优化

10Hive性能优化 1Hive性能问题排查的方式1.1Hive底层原理&#xff1a;explain执行计划详解1.1.1 explain理论1.1.2 实践 2Hive性能调优的方式2.1. SQL语句优化1. union all2. distinct 2.2. 数据格式优化2.3. 小文件过多优化2.3.1解决hive小文件过多问题小文件产生的原因小文件…

安全规约、脱敏规范、敏感逻辑的保护方案、防止 SQL 注入

文章目录 I 强制性安全规约平台资源的防重放机制URL 外部重定向传入的目标地址必须执行白名单过滤。表单、AJAX 提交必须执行 CSRF 安全验证。禁止向 HTML 页面输出未经安全过滤或未正确转义的用户数据。用户请求参数必须做有效性验证防止 SQL 注入用户敏感数据用户权限控制校验…

【数学】概率论与数理统计(五)

文章目录 [toc] 二维随机向量及其分布随机向量离散型随机向量的概率分布律性质示例问题解答 连续型随机向量的概率密度函数随机向量的分布函数性质连续型随机向量均匀分布 边缘分布边缘概率分布律边缘概率密度函数二维正态分布示例问题解答 边缘分布函数 二维随机向量及其分布 …

3.Qt Quick-QML地图引擎之v4.3版本(新增动态轨迹线/海图/天地图街道/天地图卫星)

在上个版本Qt Quick-QML地图引擎之v4版本(新增多模型切换/3D模型欧拉角模拟)_qt加载3d地图-CSDN博客更新了3D模拟功能&#xff0c;在4.3版本增加动态轨迹线、三个地图(海图/天地图街道/天地图卫星)。 4.3版本已经支持qt6 cmake版本&#xff0c;而4.3版本以下支持qt5版本&#x…

数据结构:DisjointSet

Disjoint Sets意思是一系列没有重复元素的集合。一种常见的实现叫做&#xff0c;Disjoint-set Forest可以以接近常数的时间复杂度查询元素所属集合&#xff0c;用来确定两个元素是否同属一个集合等&#xff0c;是效率最高的常见数据结构之一。 Wiki链接&#xff1a;https://en…

vmware-ubuntu22.04配置虚拟机win10,重新上网成功

打开问题显示 Hardware配置 Options配置 最后的Advanced&#xff0c;第一次用了BIOS&#xff0c;然后启动中有更新&#xff0c;然后关闭&#xff0c;再用UEFI启动

【2024年华为OD机试】(B卷,100分)- 数组去重和排序(Java JS PythonC/C++)

一、问题描述 题目描述 给定一个乱序的数组&#xff0c;删除所有的重复元素&#xff0c;使得每个元素只出现一次&#xff0c;并且按照出现的次数从高到低进行排序&#xff0c;相同出现次数按照第一次出现顺序进行先后排序。 输入描述 一个数组 输出描述 去重排序后的数组…

《零基础Go语言算法实战》【题目 2-10】接口的实现

《零基础Go语言算法实战》 【题目 2-10】接口的实现 请指出下面代码中存在的问题。 type Programmer struct { Name string } func (p *Programmer) String() string { return fmt.Sprintf("print: %v", p) } func main() { p : &Programmer{} p.String()…