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

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

1. 问题描述

我发现通过dubbo调用某类接口时,若该接口的返回类型是Collection的子类并且扩展了自实现的属性, 则返回结果集会丢失自实现的属性。 比如, 我有个接口方法返回PageList(源码如下),则当客户端调用这个方法时,得到的返回对象中并没有totalCount。 Dubbo序列化的Bug及相应修复过程_第1张图片

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);
        else
            hasEnd = 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);
        else
            hasEnd = out.writeListBegin(list.size(), obj.getClass().getName());

              /**
         * 修改序列化过程丢失属性的bug, 对继承自Collection并扩展了新属性的类,对其新增属性序列化。

         *
         * Added By HuQingmiao([email protected]) 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-28
                boolean 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-29
                Class 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
 *    [email protected].
 *
 * 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([email protected]) 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-28
                boolean 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-29
                Class 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([email protected]) 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-28
                boolean 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-29
                Class 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

你可能感兴趣的:(Dubbo序列化的Bug及相应修复过程)