2019独角兽企业重金招聘Python工程师标准>>>
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
}
/** 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
}
对以上修改后的代码进行测试,发现在反序列化时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 。