【Dubbo】默认hession2反序列化机制导致dubbo接口返回HashMap

问题描述

  • 在使用dubbo调用接口的时候,莫名其妙出现java.lang.ClassCastException: java.util.HashMap cannot be cast to xxxx异常
  • 经过排查发现,是因为dubbo接口返回的不是xxxx对象,而是HashMap

源码分析

  • dubbo的反序列化机制默认是hessian2
  • 首先定位到SerializerFactory类的getDeserializer()方法
try {
//  Class cl = Class.forName(type, false, _loader);Class cl = loadSerializedClass(type);deserializer = getDeserializer(cl);
} catch (Exception e) {log.warning("Hessian/Burlap: '" + type + "' is an unknown class in " + _loader + ":\n" + e);_typeNotFoundDeserializerMap.put(type, PRESENT);log.log(Level.FINER, e.toString(), e);_unrecognizedTypeCache.put(type, new AtomicLong(1L));
}
  • 断点发现deserializer = getDeserializer(cl);这一行代码抛了异常,所以能看到日志输出
log.warning("Hessian/Burlap: '" + type + "' is an unknown class in " + _loader + ":\n" + e);
  • 然后手工试了Class.forName,确实会报java.lang.ClassNotFoundException异常
  • 那问题就与类加载器有关了,看看hessian2的类加载器是哪个
  • 打开org.apache.dubbo.common.serialize.hessian2.Hessian2SerializerFactory这个类
public class Hessian2SerializerFactory extends SerializerFactory {@Overridepublic ClassLoader getClassLoader() {return Thread.currentThread().getContextClassLoader();}
}
  • Hessian2重写了类加载器,为了观察这是哪个类加载器,增加日志输出
public class Hessian2SerializerFactory extends SerializerFactory {@Overridepublic ClassLoader getClassLoader() {ClassLoader classLoaderTest = Thread.currentThread().getContextClassLoader();loadClass(classLoaderTest, "com.xxxx");ClassLoader parent = classLoaderTest.getParent();while(parent!=null) {loadClass(classLoaderTest, "com.xxxx");parent = parent.getParent();}return classLoaderTest;}private void loadClass(ClassLoader classLoader, String className) {try {classLoader.loadClass(className);log.info("load success: {}", classLoader.getClass().getName());} catch (ClassNotFoundException e) {log.info("load fail: {}", classLoader.getClass().getName());}}
}
  • 经过测试发现,能正常加载类的类加载器是org.springframework.boot.web.embedded.tomcat.TomcatEmbeddedWebappClassLoaderorg.springframework.boot.loader.LaunchedURLClassLoader,两个类加载器都是Spring
  • 而不可以加载到类的类加载器是jdk.internal.loader.ClassLoaders.AppClassLoader
  • 所以,到了这里,问题就很明显了,dubbo消费者在反序列化的时候,由于加载不到这个类,所以返回的对象按HashMap处理

解决方案

  • 参考org.springframework.util.ClassUtils工具类的getDefaultClassLoader()方法
@Nullable
public static ClassLoader getDefaultClassLoader() {ClassLoader cl = null;try {cl = Thread.currentThread().getContextClassLoader();}catch (Throwable ex) {// Cannot access thread context ClassLoader - falling back...}if (cl == null) {// No thread context class loader -> use class loader of this class.cl = ClassUtils.class.getClassLoader();if (cl == null) {// getClassLoader() returning null indicates the bootstrap ClassLoadertry {cl = ClassLoader.getSystemClassLoader();}catch (Throwable ex) {// Cannot access system ClassLoader - oh well, maybe the caller can live with null...}}}return cl;
}
  • 如果Thread.currentThread().getContextClassLoader()返回的是JDK类加载器
  • 那么就使用ClassUtils.class.getClassLoader()代替
  • 修改后的代码如下
    @Overridepublic ClassLoader getClassLoader() {ClassLoader classLoader = ClassUtils.getDefaultClassLoader();if(Objects.nonNull(classLoader) && classLoader.getClass().getName().contains("spring")) {return classLoader;}ClassLoader classLoaderNew = ClassUtils.class.getClassLoader();log.info("classLoader is wrong: {}, change to: {}", classLoader, classLoaderNew);return classLoaderNew;}
  • 可以看到日志输出
[2023-12-20 18:56:48.683] [INFO] [ForkJoinPool.commonPool-worker-84]c.m.v.i.d.h.xxxxHessian2SerializerFactory[56][]- classLoader is wrong: jdk.internal.loader.ClassLoaders$AppClassLoader@73d16e93, change to: org.springframework.boot.loader.LaunchedURLClassLoader@14514713
  • 最终dubbo接口可以正常返回Java对象,而不是HashMap

利用dubbo的SPI机制

  • resources目录下新建文件META-INF/dubbo/org.apache.dubbo.common.serialize.hessian2.dubbo.Hessian2FactoryInitializer
  • 文件内容如下
default=xxxx.xxxxHessian2FactoryInitializer
  • 这样可以指定自定义的Hessian2FactoryInitializer
  • 在这个Hessian2FactoryInitializer里面创建自定义的Hessian2SerializerFactory
@Slf4j
public class xxxxHessian2FactoryInitializer extends DefaultHessian2FactoryInitializer {@Overridepublic SerializerFactory getSerializerFactory() {Hessian2SerializerFactory hessian2SerializerFactory = new xxxxHessian2SerializerFactory();hessian2SerializerFactory.getClassFactory().allow(RuntimeException.class.getName());hessian2SerializerFactory.setAllowNonSerializable(Boolean.parseBoolean(System.getProperty("dubbo.hessian.allowNonSerializable", "false")));return hessian2SerializerFactory;}
}
  • 剩下的就以前面的逻辑全部串起来了

总结

  • 虽然找到了问题的原因,也解决了问题,但依然有疑问,如果启动多次,有时候能正常转成Java对象,是一个随机的状态,感觉启动的时候决定了这个hessian2的类加载器
  • 遇到一些莫名其妙的问题时,不要慌,耐心的断点,分析代码查找问题的原因
  • 解决问题时,最好深刻理解源码的设计,利用原有框架的扩展性,进行问题的修复,这样的代码是最优雅的

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

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

相关文章

【扩散模型】8、DALL-E2 | 借助 CLIP 的图文对齐能力来实现文本到图像的生成

文章目录 一、背景二、方法2.1 Decoder2.2 Prior 三、图像控制3.1 Variations3.2 Interpolations3.3 Text Diffs 四、探索 CLIP 的潜在空间五、文本到图像的生成5.1 先验的重要性5.2 人类评价5.3 多样性和保真性的平衡5.3 在 COCO 上对比 论文:DALLE.2 代码&#x…

《网络安全面试总结》--web白盒漏洞问题

网络安全面试题目 Web安全 web白盒漏洞问题 1.JAVA反序列化了解吗?有没有了解过shrio反序列化? Java中的ObjectOutputStream类的writeObject()方法可以实现序列化,其作用把对象转换成字节流,便于保存或者传输,而Ob…

JVM-12-即时编译器

Java程序最初都是通过解释器(Interpreter)进行解释执行的,当虚拟机发现某个方法或代码块的运行特别频繁,就会把这些代码认定为“热点代码”(Hot Spot Code),为了提高热点代码的执行效率&#xf…

178.【2023年华为OD机试真题(C卷)】CPU算力分配(实现JavaPythonC++JS)

🚀你的旅程将在这里启航!本专栏所有题目均包含优质解题思路,高质量解题代码,详细代码讲解,助你深入学习,深度掌握! 文章目录 【2023年华为OD机试真题(C卷)】CPU算力分配(实现Java&Python&C++&&JS)题目描述解题思路题解代码Python 题解代码Java 题解…

案例 | 数据中台如何支撑6000+门店降本提效?

对于企业来说,上中台不是目的,借助数据中台让企业建立数据驱动意识,并结合数据中台持续做好各项业务运营,才是根本。 那么对于零售行业来说,该如何利用数据中台为业务赋能?惟客数据以某头部连锁零售企业为…

leetcode 974. 和可被 K 整除的子数组(优质解法)

代码&#xff1a; class Solution {public int subarraysDivByK(int[] nums, int k) {HashMap<Integer,Integer> hashMapnew HashMap();hashMap.put(0,1);int count0; //记录子数组的个数int last0; //前一个下标的前缀和int now0; //当前下标的前缀和for(int i0;…

打开任务管理器的13种方法,总有一款适合你

任务管理器是一个很好的工具,可以帮助你管理应用程序、进程和服务在Windows PC上的运行方式。在使用任务管理器之前,你应该首先知道如何打开它。在本指南中,我们将向你展示运行它的不同方式,无论你使用的是Windows 11还是Windows 10。该列表包括启动任务管理器的十三种方法…

使用Python爬取GooglePlay并从复杂的自定义数据结构中实现解析

文章目录 【作者主页】&#xff1a;吴秋霖 【作者介绍】&#xff1a;Python领域优质创作者、阿里云博客专家、华为云享专家。长期致力于Python与爬虫领域研究与开发工作&#xff01; 【作者推荐】&#xff1a;对JS逆向感兴趣的朋友可以关注《爬虫JS逆向实战》&#xff0c;对分布…

计算机组成原理综合3

41、计算机操作的最小时间单位是__________。A A. 时钟周期 B. 指令周期 C. CPU周期 D. 外围设备 42、微程序控制器中&#xff0c;机器指令与微指令的关系是__________。B A. 每一条机器指令由一条微指令来执行 B. 每一条机器指令由一段用微指令编成…

在做题中学习(35):判断字符是否唯一

面试题 01.01. 判定字符是否唯一 - 力扣&#xff08;LeetCode&#xff09; 思路&#xff1a;1.用哈希表&#xff08;创建另一个数组存储&#xff09;然后和原数组一一比对。 时间复杂度O&#xff08;N&#xff09; 空间复杂度 O&#xff08;N&#xff09; 2.位图&#xff08…

使用物理机的burpsuite抓取虚拟机的请求包(虚拟机代理配置)

关于burpsuite抓取本地浏览器的请求包大家应该都会配置吧 我也是第一次配抓取虚拟机的包&#xff0c;最开始遇到了些问题&#xff0c;这里简单给大家分享一下 下面以Windows系统下的Firefox浏览器为例&#xff1a; 首先我还是先添加了一个小狐狸&#xff08;foxyproxy&#…

ubuntu保存分辨率失效解决办法

在VM虚拟机中&#xff0c;遇到修改ubuntu分辨率后&#xff0c;重启后又重置的解决办法。 目前我的ubuntu版本是&#xff1a;ubuntu 18.04.6 版本。 1.首先&#xff0c;在你喜欢的目录建立一个.sh 脚本文件。 终端执行命令&#xff1a;sudo vim xrandr.sh 2.按 i 进入编辑状…

神经网络:优化器和全连接层

SGD&#xff08;随机梯度下降&#xff09; 随机梯度下降的优化算法在科研和工业界是很常用的。 很多理论和工程问题都能转化成对目标函数进行最小化的数学问题。 举个例子&#xff1a;梯度下降&#xff08;Gradient Descent&#xff09;就好比一个人想从高山上奔跑到山谷最低…

[node] Node.js 缓冲区Buffer

[node] Node.js 缓冲区Buffer 什么是BufferBuffer 与字符编码Buffer 的方法概览Buffer 的实例Buffer 的创建写入缓冲区从 Buffer 区读取数据将 Buffer 转换为 JSON 对象Buffer 的合并Buffer 的比较Buffer 的覆盖Buffer 的截取--sliceBuffer 的长度writeUIntLEwriteUIntBE 什么是…

【51单片机系列】C51中的中断系统扩展实验

本文是关于51单片机中断系统的扩展实验。 文章目录 一、 扩展实验一&#xff1a;使用外部中断0控制蜂鸣器&#xff0c;外部中断1控制直流电机二、扩展实验二&#xff1a;修改定时器初值&#xff0c;设定3秒钟的定时时间让LED模块闪烁三、扩展实验三&#xff1a;使用定时器1和数…

【问题+解决】 less-loader下载

最近遇到一个less问题 情景 属于共建项目&#xff1b; 克隆下来&#xff0c;npm install&#xff0c;报错 按照提示下载了 less-loader&#xff0c;还是报错 经过 下载 npm install less-loader5.0.0 --save --dev npm install --save script-loader 都不对&#xff0c;下载成…

华为OD机试 - 区间交集 - 深度优先搜索dfs算法(滥用)(Java 2023 B卷 200分)

目录 专栏导读一、题目描述二、输入描述三、输出描述备注用例1、输入2、输出3、说明 四、解题思路1、核心思路&#xff1a;2、具体步骤 五、Java算法源码再重新读一遍题目&#xff0c;看看能否优化一下~解题步骤也简化了很多。 六、效果展示1、输入2、输出3、说明 华为OD机试 2…

Screen记录窗口输出日志

screen是Linux窗口管理器&#xff0c;用户可以建立多个screen会话&#xff0c;每个screen会话又可以建立多个window窗口&#xff0c;每一个窗口就像一个可操作的真实的ssh终端一样。 screen详解&#xff1a;http://www.linuxidc.com/Linux/2013-10/91612.htm Linux Screen超简…

laravel 安装新插件 topthink/think-migration

环境&#xff1a;本地虚拟机&#xff0c;跑docker容器&#xff0c;项目文件挂载到本地。 需进入docker容器中(本地无相关环境&#xff0c;比如swoole&#xff0c;无法执行命令。)&#xff0c;执行插件更新和使用命令执行相关的artisan命令。 比如安装插件 topthink/think-migr…

C++_动态二维数组的两种方法

介绍 本文主要介绍使用 动态二维数组的两种方法 (PS:仅作创建 动态二维数组参考,详细使用方法根据需求自行改变) 第一种&#xff1a;连续存储结构的 二维动态数组(需固定 列 大小&#xff0c;可通过下标访问) 缺点: 1.需要在设计二维数组前写死 列 的大小 2.空间利用率不高 优点…