热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Dubbo序列化的Bug及相应修复过程

2019独角兽企业重金招聘Python工程师标准1.问题描述我发现通过dubbo调用某类接口时,若该接口的返回类型是Collection的子类并且扩展了自实现的

2019独角兽企业重金招聘Python工程师标准>>> hot3.png

1. 问题描述

我发现通过dubbo调用某类接口时,若该接口的返回类型是Collection的子类并且扩展了自实现的属性, 则返回结果集会丢失自实现的属性。 比如, 我有个接口方法返回PageList(源码如下),则当客户端调用这个方法时,得到的返回对象中并没有totalCount。 输入图片说明

2. 问题定位

开启debug, 跟入源码,发现dubbo是依赖hessian完成序列化与反序列化的。以下是针对Collecltion对象的序列化类:

package com.alibaba.com.caucho.hessian.io;import java.io.IOException;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;/*** Serializing a JDK 1.2 Collection.*/
public class CollectionSerializer extends AbstractSerializer {private boolean _sendJavaType = true;/*** Set true if the java type of the collection should be sent.*/public void setSendJavaType(boolean sendJavaType) {_sendJavaType = sendJavaType;}/*** Return true if the java type of the collection should be sent.*/public boolean getSendJavaType() {return _sendJavaType;}public void writeObject(Object obj, AbstractHessianOutput out)throws IOException {if (out.addRef(obj))return;Collection list = (Collection) obj;Class cl = obj.getClass();boolean hasEnd;if (cl.equals(ArrayList.class)|| !_sendJavaType|| !Serializable.class.isAssignableFrom(cl))hasEnd = out.writeListBegin(list.size(), null);elsehasEnd = out.writeListBegin(list.size(), obj.getClass().getName());Iterator iter = list.iterator();while (iter.hasNext()) {Object value = iter.next();out.writeObject(value);}if (hasEnd)out.writeListEnd();}
}

以上代码,方法 writeObject(Object obj, AbstractHessianOutput out) 的入参obj,其实相当于以上的PageList对象。仔细看就发现这个方法只对聚集的成员进行了写入,而没有对聚集外的其它属性进行写入,从而导致属性丢失。

3. 问题解决

于是, 我尝试对以上序列化类CollectionSerializer进行修改,当然还要同步修改相应的反序列化类。 以下是修改后的代码, 已通过注释标出:

package com.alibaba.com.caucho.hessian.io;import java.io.IOException;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;/*** Serializing a JDK 1.2 Collection.*/
public class CollectionSerializer extends AbstractSerializer {private boolean _sendJavaType = true;/*** Set true if the java type of the collection should be sent.*/public void setSendJavaType(boolean sendJavaType) {_sendJavaType = sendJavaType;}/*** Return true if the java type of the collection should be sent.*/public boolean getSendJavaType() {return _sendJavaType;}public void writeObject(Object obj, AbstractHessianOutput out)throws IOException {if (out.addRef(obj))return;Collection list = (Collection) obj;Class cl = obj.getClass();boolean hasEnd;if (cl.equals(ArrayList.class)|| !_sendJavaType|| !Serializable.class.isAssignableFrom(cl))hasEnd = out.writeListBegin(list.size(), null);elsehasEnd = out.writeListBegin(list.size(), obj.getClass().getName());/*** 修改序列化过程丢失属性的bug, 对继承自Collection并扩展了新属性的类,对其新增属性序列化。** Added By HuQingmiao(443770574@qq.com) on 2017-03-25.*//** begin **/try {Class clasz = list.getClass();//记录已经写过的子类属性,以防被同名父类属性覆盖Set fieldNameSet = new HashSet();// 从当前自定义List子类逐层向上处理,对各层属性进行序列化for (; !clasz.getName().startsWith("java."); clasz = clasz.getSuperclass()) {// 如果当前类直接实现了List或Set接口,则不对其元素进行读写. 2017-08-28boolean impListOrSet = false;for (Class c : clasz.getInterfaces()) {if (List.class.equals(c) | Set.class.equals(c) | SortedSet.class.equals(c) | Collection.class.equals(c)) {impListOrSet = true;break;}}if (impListOrSet) {continue;}// 如果当前类直接继承AbstractCollection/AbstractList/ABstractSet类,则不对其元素进行读写. 2017-08-29Class sc = clasz.getSuperclass();if (AbstractList.class.equals(sc) | AbstractSet.class.equals(sc) | AbstractCollection.class.equals(sc)) {continue;}Field[] fields = clasz.getDeclaredFields();for (Field field : fields) {//log.debug(">> " + clasz.getSimpleName() + "." + field.getName() + " " + field.getType());// 子类属性已被写入,不再写入同名父属性if (fieldNameSet.contains(field.getName())) {continue;}if (Modifier.isTransient(field.getModifiers()) || Modifier.isStatic(field.getModifiers())) {continue;}boolean isAccessible = field.isAccessible();if (!isAccessible) {field.setAccessible(true);}Object val = field.get(list);//log.debug(">> "+clasz.getSimpleName()+" "+field.getName()+" "+field.getType()+" "+val);out.writeObject(val);field.setAccessible(isAccessible);// 记录已写过的属性fieldNameSet.add(field.getName());}}// end for (; !clasz.getName()fieldNameSet.clear();} catch (IllegalAccessException e) {throw new IOException(e.getMessage());}/** end **/Iterator iter = list.iterator();while (iter.hasNext()) {Object value = iter.next();out.writeObject(value);}if (hasEnd)out.writeListEnd();}
}

/** Copyright (c) 2001-2004 Caucho Technology, Inc. All rights reserved.** The Apache Software License, Version 1.1** Redistribution and use in source and binary forms, with or without* modification, are permitted provided that the following conditions* are met:** 1. Redistributions of source code must retain the above copyright* notice, this list of conditions and the following disclaimer.** 2. Redistributions in binary form must reproduce the above copyright* notice, this list of conditions and the following disclaimer in* the documentation and/or other materials provided with the* distribution.** 3. The end-user documentation included with the redistribution, if* any, must include the following acknowlegement:* "This product includes software developed by the* Caucho Technology (http://www.caucho.com/)."* Alternately, this acknowlegement may appear in the software itself,* if and wherever such third-party acknowlegements normally appear.** 4. The names "Hessian", "Resin", and "Caucho" must not be used to* endorse or promote products derived from this software without prior* written permission. For written permission, please contact* info@caucho.com.** 5. Products derived from this software may not be called "Resin"* nor may "Resin" appear in their names without prior written* permission of Caucho Technology.** THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE* DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.** @author Scott Ferguson*/package com.alibaba.com.caucho.hessian.io;import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.*;/*** Deserializing a JDK 1.2 Collection.*/
public class CollectionDeserializer extends AbstractListDeserializer {//private Logger log = LoggerFactory.getLogger(this.getClass());private Class _type;public CollectionDeserializer(Class type) {_type = type;}public Class getType() {return _type;}public Object readList(AbstractHessianInput in, int length)throws IOException {Collection list = createList();in.addRef(list);/*** 解决序列化过程丢失属性的bug,对继承自Collection并扩展了新属性的类,对其新增属性反序列化。** Added By HuQingmiao(443770574@qq.com) on 2017-03-25.*//** begin **/try {Class clasz = list.getClass();//记录已经读过的子类属性,以防被同名父类属性覆盖Set fieldNameSet = new HashSet();// 从当前自定义List子类逐层向上处理,对各层属性进行反序列化for (; !clasz.getName().startsWith("java."); clasz = clasz.getSuperclass()) {// 如果当前类直接实现了List或Set接口,则不对其元素进行读写. 2017-08-28boolean impListOrSet = false;for (Class c : clasz.getInterfaces()) {if (List.class.equals(c) | Set.class.equals(c) | SortedSet.class.equals(c) | Collection.class.equals(c)) {impListOrSet = true;break;}}if (impListOrSet) {continue;}// 如果当前类直接继承AbstractCollection/AbstractList/ABstractSet类,则不对其元素进行读写. 2017-08-29Class sc = clasz.getSuperclass();if (AbstractList.class.equals(sc) | AbstractSet.class.equals(sc) | AbstractCollection.class.equals(sc)) {continue;}Field[] fields = clasz.getDeclaredFields();for (Field field : fields) {//log.debug(">>2 " + clasz.getSimpleName() + "." + field.getName() + " " + field.getType());// 子类属性已被读取,不再读取同名父属性if (fieldNameSet.contains(field.getName())) {continue;}if (Modifier.isTransient(field.getModifiers()) || Modifier.isStatic(field.getModifiers())) {continue;}boolean isAccessible = field.isAccessible();if (!isAccessible) {field.setAccessible(true);}Object val = in.readObject();//log.debug(">>2 " + clasz.getSimpleName() + "." + field.getName() + " " + field.getType() + " " + val);field.set(list, val);field.setAccessible(isAccessible);// 记录已记取的属性fieldNameSet.add(field.getName());}}// end for (; !clasz.getName()fieldNameSet.clear();} catch (IllegalAccessException e) {throw new IOException(e.getMessage());}/** end **/while (!in.isEnd())list.add(in.readObject());in.readEnd();return list;}public Object readLengthList(AbstractHessianInput in, int length)throws IOException {Collection list = createList();in.addRef(list);/*** 解决序列化过程丢失属性的bug,对继承自Collection并扩展了新属性的类,对其新增属性反序列化。** Added By HuQingmiao(443770574@qq.com) on 2017-03-25.*//** begin **/try {Class clasz = list.getClass();//记录已经读过的子类属性,以防被同名父类属性覆盖Set fieldNameSet = new HashSet();// 从当前自定义List子类逐层向上处理,对各层属性进行反序列化for (; !clasz.getName().startsWith("java."); clasz = clasz.getSuperclass()) {// 如果当前类直接实现了List或Set接口,则不对其元素进行读写. 2017-08-28boolean impListOrSet = false;for (Class c : clasz.getInterfaces()) {if (List.class.equals(c) | Set.class.equals(c) | SortedSet.class.equals(c) | Collection.class.equals(c)) {impListOrSet = true;break;}}if (impListOrSet) {continue;}// 如果当前类直接继承AbstractCollection/AbstractList/ABstractSet类,则不对其元素进行读写. 2017-08-29Class sc = clasz.getSuperclass();if (AbstractList.class.equals(sc) | AbstractSet.class.equals(sc) | AbstractCollection.class.equals(sc)) {continue;}Field[] fields = clasz.getDeclaredFields();for (Field field : fields) {//log.debug(">>2 " + clasz.getSimpleName() + "." + field.getName() + " " + field.getType());// 子类属性已被读取,不再读取同名父属性if (fieldNameSet.contains(field.getName())) {continue;}if (Modifier.isTransient(field.getModifiers()) || Modifier.isStatic(field.getModifiers())) {continue;}boolean isAccessible = field.isAccessible();if (!isAccessible) {field.setAccessible(true);}Object val = in.readObject();//log.debug(">>2 " + clasz.getSimpleName() + "." + field.getName() + " " + field.getType() + " " + val);field.set(list, val);field.setAccessible(isAccessible);// 记录已记取的属性fieldNameSet.add(field.getName());}}// end for (; !clasz.getName()fieldNameSet.clear();} catch (IllegalAccessException e) {throw new IOException(e.getMessage());}/** end **/for (; length > 0; length--)list.add(in.readObject());return list;}private Collection createList()throws IOException {Collection list = null;if (_type == null)list = new ArrayList();else if (!_type.isInterface()) {try {list = (Collection) _type.newInstance();} catch (Exception e) {}}if (list != null) {} else if (SortedSet.class.isAssignableFrom(_type))list = new TreeSet();else if (Set.class.isAssignableFrom(_type))list = new HashSet();else if (List.class.isAssignableFrom(_type))list = new ArrayList();else if (Collection.class.isAssignableFrom(_type))list = new ArrayList();else {try {list = (Collection) _type.newInstance();} catch (Exception e) {throw new IOExceptionWrapper(e);}}return list;}
}

对以上修改后的代码进行测试,发现在反序列化时ArrayList的子类被识别成了ArrayList,为此还需要对com.alibaba.com.caucho.hessian.io.Hessian2Input 的第21270行做如下修改:

case 0x77: {int length = tag - 0x70;String type = readType();Deserializer reader;/*** Modified by HuQingmiao on 2017-04-01*/// getListDeserializer(null, cl) -> getListDeserializer(type, cl);reader = findSerializerFactory().getListDeserializer(type, cl);Object v = reader.readLengthList(this, length);return v;}

至此, 修改完毕,测试通过。

4. 关于dubbo源码

关于dubbo的bug及修改内容, 见 https://github.com/HuQingmiao/dubbo 。


转:https://my.oschina.net/HuQingmiao/blog/868264



推荐阅读
  • [大整数乘法] java代码实现
    本文介绍了使用java代码实现大整数乘法的过程,同时也涉及到大整数加法和大整数减法的计算方法。通过分治算法来提高计算效率,并对算法的时间复杂度进行了研究。详细代码实现请参考文章链接。 ... [详细]
  • Android源码深入理解JNI技术的概述和应用
    本文介绍了Android源码中的JNI技术,包括概述和应用。JNI是Java Native Interface的缩写,是一种技术,可以实现Java程序调用Native语言写的函数,以及Native程序调用Java层的函数。在Android平台上,JNI充当了连接Java世界和Native世界的桥梁。本文通过分析Android源码中的相关文件和位置,深入探讨了JNI技术在Android开发中的重要性和应用场景。 ... [详细]
  • Java太阳系小游戏分析和源码详解
    本文介绍了一个基于Java的太阳系小游戏的分析和源码详解。通过对面向对象的知识的学习和实践,作者实现了太阳系各行星绕太阳转的效果。文章详细介绍了游戏的设计思路和源码结构,包括工具类、常量、图片加载、面板等。通过这个小游戏的制作,读者可以巩固和应用所学的知识,如类的继承、方法的重载与重写、多态和封装等。 ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • 开发笔记:加密&json&StringIO模块&BytesIO模块
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了加密&json&StringIO模块&BytesIO模块相关的知识,希望对你有一定的参考价值。一、加密加密 ... [详细]
  • Spring特性实现接口多类的动态调用详解
    本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
  • 个人学习使用:谨慎参考1Client类importcom.thoughtworks.gauge.Step;importcom.thoughtworks.gauge.T ... [详细]
  • 闭包一直是Java社区中争论不断的话题,很多语言都支持闭包这个语言特性,闭包定义了一个依赖于外部环境的自由变量的函数,这个函数能够访问外部环境的变量。本文以JavaScript的一个闭包为例,介绍了闭包的定义和特性。 ... [详细]
  • Java学习笔记之面向对象编程(OOP)
    本文介绍了Java学习笔记中的面向对象编程(OOP)内容,包括OOP的三大特性(封装、继承、多态)和五大原则(单一职责原则、开放封闭原则、里式替换原则、依赖倒置原则)。通过学习OOP,可以提高代码复用性、拓展性和安全性。 ... [详细]
  • JDK源码学习之HashTable(附带面试题)的学习笔记
    本文介绍了JDK源码学习之HashTable(附带面试题)的学习笔记,包括HashTable的定义、数据类型、与HashMap的关系和区别。文章提供了干货,并附带了其他相关主题的学习笔记。 ... [详细]
  • 模板引擎StringTemplate的使用方法和特点
    本文介绍了模板引擎StringTemplate的使用方法和特点,包括强制Model和View的分离、Lazy-Evaluation、Recursive enable等。同时,还介绍了StringTemplate语法中的属性和普通字符的使用方法,并提供了向模板填充属性的示例代码。 ... [详细]
  • 面向对象之3:封装的总结及实现方法
    本文总结了面向对象中封装的概念和好处,以及在Java中如何实现封装。封装是将过程和数据用一个外壳隐藏起来,只能通过提供的接口进行访问。适当的封装可以提高程序的理解性和维护性,增强程序的安全性。在Java中,封装可以通过将属性私有化并使用权限修饰符来实现,同时可以通过方法来访问属性并加入限制条件。 ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • XML介绍与使用的概述及标签规则
    本文介绍了XML的基本概念和用途,包括XML的可扩展性和标签的自定义特性。同时还详细解释了XML标签的规则,包括标签的尖括号和合法标识符的组成,标签必须成对出现的原则以及特殊标签的使用方法。通过本文的阅读,读者可以对XML的基本知识有一个全面的了解。 ... [详细]
  • 开发笔记:Java是如何读取和写入浏览器Cookies的
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了Java是如何读取和写入浏览器Cookies的相关的知识,希望对你有一定的参考价值。首先我 ... [详细]
author-avatar
可惜偏偏孤独一个小姐_448
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有