动态代理更改Java方法的返回参数(可用于优化feign调用后R对象的统一处理)

动态代理更改Java方法的返回参数(可用于优化feign调用后R对象的统一处理)

  • 需求
  • 原始解决方案
  • 优化后方案
    • 1.首先创建AfterInterface.java
    • 2.创建InvocationHandler处理代理方法
    • 3. 调用
  • 实际运行场景
  • 拓展

需求

某些场景,调用别人的方法,但是它外面包了一层,我们只需要里面实际的数据,例如后端开发中的R对象,实际最终只需要data。也就是说可以看成,调用原始方法,代理后更改为别的类型。
下面是R对象,实际运用大同小异,方便大家理解。

public class R<T> implements Serializable {private static final long serialVersionUID = 1L;/*** 正确返回码*/public static final String SUCCESS_CODE = "0";/*** 返回码*/private String code;/*** 返回消息*/private String message;/*** 响应数据*/private T data;/*** 请求ID*/private String requestId;public boolean isSuccess() {return SUCCESS_CODE.equals(code);}
}

下面仅仅举例,重点看出怎么转换返回类型的,没有实际意义。具体使用场景可参考后面可参考具体在实际运用场景的

原始解决方案

PreInterface.java 模拟原始方法返回值。

package com.zdh.proxy.chagemethod;/*** @author zdh* @date 2024/07/24* @desc*/
public interface PreInterface {default double method1() {return 2.3;}default int method2() {return 123;}default boolean method3() {return true;}
}

新建一个PreManager 类,获得PreInterface对象,重新封装所有方法。

package com.zdh.proxy.chagemethod;/*** @author developer_ZhangXinHua* @date 2024/07/24* @desc (详细信息)*/
public class PreManager {//正常从容器中拿PreInterface afterInterface = new PreInterface(){};public String method1() {return "manager: "+ afterInterface.method1();}public String method2() {return "manager: "+ afterInterface.method2();}public String method3() {return "manager: "+ afterInterface.method3();}public static void main(String[] args) {PreManager preManager = new PreManager();System.out.println(preManager.method1());System.out.println(preManager.method2());System.out.println(preManager.method3());}
}

缺点显而易见,每个原始PreInterface就需要对应实现一个PreManager,而且需要重新实现每个方法。

优化后方案

PreInterface.java 与上面一样,不再给出。

1.首先创建AfterInterface.java

重点:因为后面代理的时候需要用到方法名和参数列表进行调用,所以方法名和参数列表一定要与PreInterface的对应的方法名相同。

package com.zdh.proxy.chagemethod;/*** @author zdh* @date 2024/07/24* @desc*/
public interface AfterInterface {public String method1();public String method2();public String method3();
}

2.创建InvocationHandler处理代理方法

package com.zdh.proxy.chagemethod;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;/*** @author zdh* @date 2024/07/24* @desc*/
public class MethodProxyDhHandler implements InvocationHandler {private final Object target;public MethodProxyDhHandler(Object target) {this.target = target;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {//根据反射,拿到和代理后的方法同名且方法参数相同的方法Method targetMethod = target.getClass().getMethod(method.getName(), method.getParameterTypes());Object ob= targetMethod.invoke(target, args);return "proxy after:"+ob;}public static <T> T create(Class<T> interfaceClass, Object target) {return (T) Proxy.newProxyInstance(interfaceClass.getClassLoader(),new Class[]{interfaceClass},new MethodProxyDhHandler(target));}}

3. 调用

    public static void main(String[] args) {PreInterface preTarget = new PreInterface() {};AfterInterface afterInterface = create(AfterInterface.class, preTarget);String s1 = afterInterface.method1();System.out.println("s1 = " + s1);String s2 = afterInterface.method2();System.out.println("s2 = " + s2);String s3 = afterInterface.method3();System.out.println("s3 = " + s3);}

可以看到,已经全部转成了String类型。这里只是测试,如果使用Spring等框架,可以直接从容器中获取afterInterface ,然后afterInterface 创建代理到容器中。
在这里插入图片描述

实际运行场景

上述方式仅仅为了简化大家的理解,那么现在有个疑问,上述方式有啥用呢。目前我遇到场景可用于优化feign调用后R对象的统一处理 (仅适用公司内部,R对象都统一),获取到R对象,根据错误码等判断成功与否,若成功可以直接拆掉直接返回data。

R.java
后端controller返回参数,大同小异。

package cn.zdh;import lombok.Data;
import lombok.experimental.Accessors;import java.io.Serializable;@Data
@Accessors(chain = true)
public class R<T> implements Serializable {private static final long serialVersionUID = 1L;/*** 正确返回码*/public static final String SUCCESS_CODE = "0";/*** 返回码*/private String code;/*** 返回消息*/private String message;/*** 响应数据*/private T data;/*** 请求ID*/private String requestId;public boolean isSuccess() {return SUCCESS_CODE.equals(code);}/*** 构造带返回数据的成功响应*//*** 构造带返回数据的成功响应*/public static <T> R<T> success(T data) {return new R<T>().setCode(R.SUCCESS_CODE).setData(data);}
}

下面ExampleClientExampleClientImpl 仅用于模拟feign远程调用。正常项目里面只需要一个ExampleClient 接口

package cn.zdh.client;import cn.zdh.R;import java.util.ArrayList;
import java.util.List;/*** @author zdh* @date 2024/07/24* @desc 模拟feign远程调用*/
public interface ExampleClient {default R<String> find1() {return R.success("f1");}default R<List<String>> find2() {List<String> list = new ArrayList<String>();list.add("a");list.add("b");return R.success(list);}default R<Double> find3(Double d) {return R.success(d);}}
package cn.zdh.client;import org.springframework.stereotype.Component;/*** @author developer_ZhangXinHua* @date 2024/07/24* @desc (详细信息)*/
@Component
public class ExampleClientImpl implements ExampleClient{
}

AfterExampleClient 拆掉R之后的data作为方法的返回类型,注意方法名和参数要与ExampleClient 方法名和参数一 一对应。简单来讲,复制粘贴,把返回值删掉R。

package cn.zdh.afterclient;import java.util.List;/*** @author developer_ZhangXinHua* @date 2024/07/23* @desc 定义需要代理后的方法*/
public interface AfterExampleClient {String find1();List<String> find2();Double find3(Double d);
}

ClientProxyDhHandler feign调用其他微服务接口,统一解析代理处理器
可以看到invoke方法中对R对象进行了统一处理,并且后续根据需要,可以通过错误码进行日志输出和报错,通过全局异常处理器,返回前端。

package cn.zdh.proxy;//package com.zdh.proxyfeign;import cn.zdh.R;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;/*** @author zdh* @date 2024/07/24* @desc  feign调用其他系统接口,统一解析代理处理器*/
public class ClientProxyDhHandler implements InvocationHandler {private final Object target;public ClientProxyDhHandler(Object target) {this.target = target;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {Method targetMethod = target.getClass().getMethod(method.getName(), method.getParameterTypes());R<?> r = (R<?>) targetMethod.invoke(target, args);if (r != null && r.isSuccess()) {return r.getData();}/*后续可以根据错误码进行日志输出和报错,通过全局异常处理器,返回前端。*/return null;}public static <T> T create(Class<T> interfaceClass, Object target) {return (T) Proxy.newProxyInstance(interfaceClass.getClassLoader(),new Class[]{interfaceClass},new ClientProxyDhHandler(target));}
}

ExampleService 模拟service层的调用

package cn.zdh.service;import cn.zdh.afterclient.AfterExampleClient;
import cn.zdh.client.ExampleClient;
import cn.zdh.proxy.ClientProxyDhHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import java.util.List;/*** @author developer_ZhangXinHua* @date 2024/07/23* @desc  模拟调用代理后的对象。*/
@Service
public class ExampleService{private final AfterExampleClient afterExampleClient;@Autowiredpublic ExampleService(ExampleClient exampleClient) {this.afterExampleClient = ClientProxyDhHandler.create(AfterExampleClient.class, exampleClient);}public void testAll(){//假设这里调用所有String f1 = afterExampleClient.find1();System.out.println("f1 = " + f1);List<String> f2 = afterExampleClient.find2();System.out.println("f2 = " + f2);Double f3 = afterExampleClient.find3(3.2);System.out.println("f3 = " + f3);}}

Spring Test 测试

@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest
@ActiveProfiles(value = "test")
public class ZdhTest {@Autowiredprivate ExampleService exampleService;@Testpublic void test1(){exampleService.testAll();}}

测试结果
在这里插入图片描述
总结,每新增一个Client(feign的微服务调用接口)仅需要创建一个与其对应的AfterClient接口 。需要使用的service,只需要使用动态代理,传入ClientProxyDhHandler并注入到容器中,即可完成统一的远程调用处理。

拓展

如下还有一个小问题,此方式注入到Spring容器中,每次使用者都需要创建代理对象,很麻烦。

@Autowired
public ExampleService(ExampleClient exampleClient) {this.afterExampleClient = ClientProxyDhHandler.create(AfterExampleClient.class, exampleClient);
}

解决方案:通过配置类,一次配置,其他使用者直接进行注入。如下:

@Configuration
public class AfterClientConfiguration {@Beanpublic AfterExampleClient afterExampleClient(ExampleClient exampleClient) {AfterExampleClient afterExampleClient = ClientProxyDhHandler.create(AfterExampleClient.class, exampleClient);return afterExampleClient;}
}

至此,优化完成。🎉🎊

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

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

相关文章

深入浅出WebRTC—DelayBasedBwe

WebRTC 中的带宽估计是其拥塞控制机制的核心组成部分&#xff0c;基于延迟的带宽估计是其中的一种策略&#xff0c;它主要基于延迟变化推断出可用的网络带宽。 1. 总体架构 1.1. 静态结构 1&#xff09;DelayBasedBwe 受 GoogCcNetworkController 控制&#xff0c;接收其输入…

贪心算法(算法篇)

算法之贪心算法 贪心算法 概念&#xff1a; 贪心算法是一种思想&#xff0c;并不是一种算法&#xff0c;贪心算法是分阶段地工作&#xff0c;在每一个阶段&#xff0c;可以认为所作决定是好的&#xff0c;而不考虑将来地后果。算法的每个阶段总是选择当前阶段最优&#xff0…

Kafka Producer之数据重复和乱序问题

文章目录 1. 数据重复2. 数据乱序 为了可靠性&#xff0c;Kafka有消息重试机制&#xff0c;但是同时也带来了2大问题 1. 数据重复 消息发送到broker后&#xff0c;broker记录消息数据到log中&#xff0c;但是由于网络问题&#xff0c;producer没有收到acks&#xff0c;于是再次…

Axure设计之轮播图(动态面板+中继器)

轮播图&#xff08;Carousel&#xff09;是一种网页或应用界面中常见的组件&#xff0c;用于展示一系列的图片或内容&#xff0c;通常通过自动播放或用户交互&#xff08;如点击箭头按钮&#xff09;来切换展示不同的内容。轮播图能够吸引用户的注意力&#xff0c;有效展示重要…

新手小白的pytorch学习第十一弹-----Computer Vision创建基础模型使用FashionMNIST

目录 PyTorch Computer Vision0 PyTorch 中 Computer vision 的库1 获得一个数据集1.1 查看数据的输入和输出形状1.2 可视化数据 2 准备 DataLoader3 Model 0: 创建一个 baseline model3.1 设置损失函数、优化器和评估指标3.2 创建一个函数来给我们的实验计时3.3 在批量数据集上…

萝卜快跑:自动驾驶的先锋与挑战

萝卜快跑&#xff1a;自动驾驶的先锋与挑战 近段时间&#xff0c;由萝卜快跑引发的自动驾驶事件如火如荼&#xff0c;成为科技领域的热门话题。萝卜快跑作为自动驾驶领域的重要参与者&#xff0c;其最新事件引发了广泛的关注和讨论。 萝卜快跑是百度推出的自动驾驶出行服务平台…

20240724-然后用idea创建一个Java项目/配置maven环境/本地仓储配置

1.创建一个java项目 &#xff08;1&#xff09;点击页面的create project&#xff0c;然后next &#xff08;2&#xff09;不勾选&#xff0c;继续next &#xff08;3&#xff09;选择新项目名称&#xff0c;新项目路径&#xff0c;然后Finsh&#xff0c;在新打开的页面选择…

Hadoop、Hive、HBase、数据集成、Scala阶段测试

姓名&#xff1a; 总分&#xff1a;Hadoop、Hive、HBase、数据集成、Scala阶段测试 一、选择题&#xff08;共20道&#xff0c;每道0.5分&#xff09; 1、下面哪个程序负责HDFS数据存储&#xff08; C &#xff09; A. NameNode B. Jobtracher C. DataNode D. Sec…

鸿蒙界面开发

界面开发 //构建 → 界面 build() {//行Row(){//列Column(){//文本 函数名(参数) 对象.方法名&#xff08;参数&#xff09; 枚举名.变量名Text(this.message).fontSize(40)//设置文本大小.fontWeight(FontWeight.Bold)//设置文本粗细.fontColor(#ff2152)//设置文本颜色}.widt…

3.JAVA-IDEA

IDEA介绍 下载安装 实际操作 免费试用&#xff0c;可以选第一个自己找到密匙开锁 首先新建project项目 建立空项目 起名并存储位置选择 确定创建项目 成功新建项目&#xff0c;开始新建模块 新建或导入模块 新建java模块 修改名称&#xff0c;位置保持默认 同样yes建立 ok即可 …

2 YOLO8的使用

1 介绍 YOLOv8是YOLO (You Only Look Once) 目标检测模型系列的最新版本&#xff0c;由Ultralytics公司开发和维护。YOLOv8是在先前版本的基础上进行的重大更新&#xff0c;不仅提升了性能&#xff0c;还增加了更多的功能&#xff0c;它不仅能够进行目标检测&#xff0c;还能完…

职业教育综合布线实验实训室建设应用案例

在信息技术迅猛发展的今天&#xff0c;综合布线技术已成为智能建筑、数据中心等基础设施不可或缺的一部分。唯众&#xff0c;作为职业教育领域的先行者&#xff0c;早在多年前便洞悉行业趋势&#xff0c;率先涉足综合布线实训室的建设&#xff0c;凭借丰富的经验和持续的创新&a…

phpstorm配置xdebug3

查看php路径相关信息 php --ini安装xdebug https://www.jetbrains.com/help/phpstorm/2024.1/configuring-xdebug.html?php.debugging.xdebug.configure php.ini 配置 在最后添加&#xff0c;以下是我的配置 [xdebug] zend_extension/opt/homebrew/Cellar/php8.1/8.1.29/p…

决策树 和 集成学习、随机森林

决策树是非参数学习算法&#xff0c;可以解决分类问题&#xff0c;天然可以解决多分类问题&#xff08;不同于逻辑回归或者SVM&#xff0c;需要通过OVR&#xff0c;OVO的方法&#xff09;&#xff0c;也可以解决回归问题&#xff0c;甚至是多输出任务&#xff0c;并且决策树有非…

【北京迅为】《i.MX8MM嵌入式Linux开发指南》-第三篇 嵌入式Linux驱动开发篇-第五十一章 添加设备树节点

i.MX8MM处理器采用了先进的14LPCFinFET工艺&#xff0c;提供更快的速度和更高的电源效率;四核Cortex-A53&#xff0c;单核Cortex-M4&#xff0c;多达五个内核 &#xff0c;主频高达1.8GHz&#xff0c;2G DDR4内存、8G EMMC存储。千兆工业级以太网、MIPI-DSI、USB HOST、WIFI/BT…

通过4G模块EC600N向阿里云物联网平台物模型上面发送字符串,现在发送int数据是成功的,发送字符串就是不成功

&#x1f3c6;本文收录于《CSDN问答解惑-专业版》专栏&#xff0c;主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案&#xff0c;希望能够助你一臂之力&#xff0c;帮你早日登顶实现财富自由&#x1f680;&#xff1b;同时&#xff0c;欢迎大家关注&&收…

轻量化YOLOv7系列:结合G-GhostNet | 适配GPU,华为诺亚提出G-Ghost方案升级GhostNet

轻量化YOLOv7系列&#xff1a;结合G-GhostNet | 适配GPU&#xff0c;华为诺亚提出G-Ghost方案升级GhostNet 需要修改的代码models/GGhostRegNet.py代码 创建yaml文件测试是否创建成功 本文提供了改进 YOLOv7注意力系列包含不同的注意力机制以及多种加入方式&#xff0c;在本文…

【Python】Facebook开源时间序列数据预测模型Prophet

文章目录 一、简介二、项目的文件解读三、Prophet类主要方法和参数3.1 主要参数3.2 主要方法 四、用法示例 一、简介 Prophet 是由 Facebook 开发的一个开源工具&#xff0c;用于时间序列数据的预测。它特别适用于处理具有强季节性和趋势的时间序列数据&#xff0c;并且对节假…

大数据之Oracle同步Doris数据不一致问题

数据同步架构如下&#xff1a; 出现的问题&#xff1a; doris中的数据条数 源库中的数据条数 总数完全不一致。 出现问题的原因&#xff1a; 在Dinky中建立表结构时&#xff0c;缺少对主键属性的限制 primary key(ID) not enforced 加上如上语句&#xff0c;数据条数解决一致 …

App Instance 架构示例

前言 在Unity程序设计过程中&#xff0c;我们处理的第一个对象是Application Instance。 它的主要职责是启动流程管理、卸载流程管理&#xff0c;次要职责是管理在内部的子系统生命周期。其他职责&#xff0c;提供或桥接应用程序的配置信息、及其他第三方接口。 它通常以单例的…