前言
公司使用springcloud搭建了一个分布式框架。但是在框架之间调用的时候,如果返回结果的真实类型涉及到泛型,就会遇到类型擦除的问题。这个问题的解决方案是,框架中数据传输时候序列化和反序列化的时候,需要获取数据的真实类型,来进行反序列化。
序列化的方案
1. 使用java的IO进行序列化和反序列化
2.使用json来进行序列化和反序列化
3.使用hessian来进行序列化和反序列化
4.使用dubbo协议来进行序列化和反序列化
虽然有很多序列化和反序列化方案,但是想要写出能够解决泛型擦除问题的代码还是非常困难的。
代码
翻阅大量资料之后,终于在dubbo框架的dubbo-common源码中找到了具体实现。
代码可以参考dubbo-common包,该jar包是阿里的dubbo框架的common,里面有上述所有方案的具体实现。
这里仅仅展示FastJson的实现
FastJsonSerialization.java
/*
* Copyright 1999-2011 Alibaba Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.dubbo.common.serialize.support.json;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import com.alibaba.dubbo.common.URL;
import com.alibaba.dubbo.common.serialize.ObjectInput;
import com.alibaba.dubbo.common.serialize.ObjectOutput;
import com.alibaba.dubbo.common.serialize.Serialization;
/**
* FastJsonSerialization
*
* @author william.liangf
*/
public class FastJsonSerialization implements Serialization {
public byte getContentTypeId() {
return 6;
}
public String getContentType() {
return "text/json";
}
public ObjectOutput serialize(URL url, OutputStream output) throws IOException {
return new FastJsonObjectOutput(output);
}
public ObjectInput deserialize(URL url, InputStream input) throws IOException {
return new FastJsonObjectInput(input);
}
}
FastJsonObjectOutput.java
/*
* Copyright 1999-2011 Alibaba Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.dubbo.common.serialize.support.json;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.Writer;
import com.alibaba.dubbo.common.serialize.ObjectOutput;
import com.alibaba.fastjson.serializer.JSONSerializer;
import com.alibaba.fastjson.serializer.SerializeWriter;
import com.alibaba.fastjson.serializer.SerializerFeature;
/**
* JsonObjectOutput
*
* @author william.liangf
*/
public class FastJsonObjectOutput implements ObjectOutput {
private final PrintWriter writer;
public FastJsonObjectOutput(OutputStream out) {
this(new OutputStreamWriter(out));
}
public FastJsonObjectOutput(Writer writer) {
this.writer = new PrintWriter(writer);
}
public void writeBool(boolean v) throws IOException {
writeObject(v);
}
public void writeByte(byte v) throws IOException {
writeObject(v);
}
public void writeShort(short v) throws IOException {
writeObject(v);
}
public void writeInt(int v) throws IOException {
writeObject(v);
}
public void writeLong(long v) throws IOException {
writeObject(v);
}
public void writeFloat(float v) throws IOException {
writeObject(v);
}
public void writeDouble(double v) throws IOException {
writeObject(v);
}
public void writeUTF(String v) throws IOException {
writeObject(v);
}
public void writeBytes(byte[] b) throws IOException {
writer.println(new String(b));
}
public void writeBytes(byte[] b, int off, int len) throws IOException {
writer.println(new String(b, off, len));
}
public void writeObject(Object obj) throws IOException {
SerializeWriter out = new SerializeWriter();
JSONSerializer serializer = new JSONSerializer(out);
serializer.config(SerializerFeature.WriteEnumUsingToString, true);
serializer.write(obj);
out.writeTo(writer);
writer.println();
writer.flush();
}
public void flushBuffer() throws IOException {
writer.flush();
}
}
FastJsonObjectInput.java
/*
* Copyright 1999-2011 Alibaba Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.dubbo.common.serialize.support.json;
import java.io.BufferedReader;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.lang.reflect.Type;
import com.alibaba.dubbo.common.serialize.ObjectInput;
import com.alibaba.dubbo.common.utils.PojoUtils;
import com.alibaba.fastjson.JSON;
/**
* JsonObjectInput
*
* @author william.liangf
*/
public class FastJsonObjectInput implements ObjectInput {
private final BufferedReader reader;
public FastJsonObjectInput(InputStream in){
this(new InputStreamReader(in));
}
public FastJsonObjectInput(Reader reader){
this.reader = new BufferedReader(reader);
}
public boolean readBool() throws IOException {
try {
return readObject(boolean.class);
} catch (ClassNotFoundException e) {
throw new IOException(e.getMessage());
}
}
public byte readByte() throws IOException {
try {
return readObject( byte.class);
} catch (ClassNotFoundException e) {
throw new IOException(e.getMessage());
}
}
public short readShort() throws IOException {
try {
return readObject(short.class);
} catch (ClassNotFoundException e) {
throw new IOException(e.getMessage());
}
}
public int readInt() throws IOException {
try {
return readObject(int.class);
} catch (ClassNotFoundException e) {
throw new IOException(e.getMessage());
}
}
public long readLong() throws IOException {
try {
return readObject(long.class);
} catch (ClassNotFoundException e) {
throw new IOException(e.getMessage());
}
}
public float readFloat() throws IOException {
try {
return readObject(float.class);
} catch (ClassNotFoundException e) {
throw new IOException(e.getMessage());
}
}
public double readDouble() throws IOException {
try {
return readObject(double.class);
} catch (ClassNotFoundException e) {
throw new IOException(e.getMessage());
}
}
public String readUTF() throws IOException {
try {
return readObject(String.class);
} catch (ClassNotFoundException e) {
throw new IOException(e.getMessage());
}
}
public byte[] readBytes() throws IOException {
return readLine().getBytes();
}
public Object readObject() throws IOException, ClassNotFoundException {
String json = readLine();
return JSON.parse(json);
}
public <T> T readObject(Class<T> cls) throws IOException, ClassNotFoundException {
String json = readLine();
return JSON.parseObject(json, cls);
}
@SuppressWarnings("unchecked")
public <T> T readObject(Class<T> cls, Type type) throws IOException,ClassNotFoundException
{
Object value = readObject(cls);
return (T) PojoUtils.realize(value, cls, type);
}
private String readLine() throws IOException, EOFException {
String line = reader.readLine();
if(line == null || line.trim().length() == 0) throw new EOFException();
return line;
}
}
PojoUtils.java
/*
* Copyright 1999-2011 Alibaba Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.dubbo.common.utils;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Proxy;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.IdentityHashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.TreeMap;
import java.util.WeakHashMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ConcurrentSkipListMap;
/**
* PojoUtils. Travel object deeply, and convert complex type to simple type.
* <p>
* Simple type below will be remained:
* <ul>
* <li> Primitive Type, also include <b>String</b>, <b>Number</b>(Integer, Long), <b>Date</b>
* <li> Array of Primitive Type
* <li> Collection, eg: List, Map, Set etc.
* </ul>
* <p>
* Other type will be covert to a map which contains the attributes and value pair of object.
*
* @author william.liangf
* @author ding.lid
*/
public class PojoUtils {
private static final ConcurrentMap<String, Method> NAME_METHODS_CACHE = new ConcurrentHashMap<String, Method>();
private static final ConcurrentMap<Class<?>, ConcurrentMap<String, Field>> CLASS_FIELD_CACHE =
new ConcurrentHashMap<Class<?>, ConcurrentMap<String, Field>>();
public static Object[] generalize(Object[] objs) {
Object[] dests = new Object[objs.length];
for (int i = 0; i < objs.length; i ++) {
dests[i] = generalize(objs[i]);
}
return dests;
}
public static Object[] realize(Object[] objs, Class<?>[] types) {
if (objs.length != types.length)
throw new IllegalArgumentException("args.length != types.length");
Object[] dests = new Object[objs.length];
for (int i = 0; i < objs.length; i ++) {
dests[i] = realize(objs[i], types[i]);
}
return dests;
}
public static Object[] realize(Object[] objs, Class<?>[] types, Type[] gtypes) {
if (objs.length != types.length
|| objs.length != gtypes.length)
throw new IllegalArgumentException("args.length != types.length");
Object[] dests = new Object[objs.length];
for (int i = 0; i < objs.length; i ++) {
dests[i] = realize(objs[i], types[i], gtypes[i]);
}
return dests;
}
public static Object generalize(Object pojo) {
return generalize(pojo, new IdentityHashMap<Object, Object>());
}
@SuppressWarnings("unchecked")
private static Object generalize(Object pojo, Map<Object, Object> history) {
if (pojo == null) {
return null;
}
if (pojo instanceof Enum<?>) {
return ((Enum<?>)pojo).name();
}
if (pojo.getClass().isArray()
&& Enum.class.isAssignableFrom(
pojo.getClass().getComponentType())) {
int len = Array.getLength(pojo);
String[] values = new String[len];
for (int i = 0; i < len; i ++) {
values[i] = ((Enum<?>)Array.get(pojo, i)).name();
}
return values;
}
if (ReflectUtils.isPrimitives(pojo.getClass())) {
return pojo;
}
if (pojo instanceof Class) {
return ((Class)pojo).getName();
}
Object o = history.get(pojo);
if(o != null){
return o;
}
history.put(pojo, pojo);
if (pojo.getClass().isArray()) {
int len = Array.getLength(pojo);
Object[] dest = new Object[len];
history.put(pojo, dest);
for (int i = 0; i < len; i ++) {
Object obj = Array.get(pojo, i);
dest[i] = generalize(obj, history);
}
return dest;
}
if (pojo instanceof Collection<?>) {
Collection<Object> src = (Collection<Object>)pojo;
int len = src.size();
Collection<Object> dest = (pojo instanceof List<?>) ? new ArrayList<Object>(len) : new HashSet<Object>(len);
history.put(pojo, dest);
for (Object obj : src) {
dest.add(generalize(obj, history));
}
return dest;
}
if (pojo instanceof Map<?, ?>) {
Map<Object, Object> src = (Map<Object, Object>)pojo;
Map<Object, Object> dest= createMap(src);
history.put(pojo, dest);
for (Map.Entry<Object, Object> obj : src.entrySet()) {
dest.put(generalize(obj.getKey(), history), generalize(obj.getValue(), history));
}
return dest;
}
Map<String, Object> map = new HashMap<String, Object>();
h