泛型学习目录:
Java泛型-1(泛型的定义)
Java泛型-2(通配符)
Java泛型-3(实践篇-protostuff序列化与反序列化)
Java泛型-4(类型擦除后如何获取泛型参数)
2. protostuff的准备工作
Java有一个序列化的技术,就是把Object转换为可保存,可传输的流数据。
而Protostuff
就是一个优秀的序列化框架。
1. 首先引入MAVEN依赖:
<dependency>
<groupId>io.protostuff</groupId>
<artifactId>protostuff-runtime</artifactId>
</dependency>
<dependency>
<groupId>io.protostuff</groupId>
<artifactId>protostuff-core</artifactId>
</dependency>
2. protostuff序列化和反序列化的问题:
使用protostuff
的时候会遇到一些无法序列化/反序列化的对象,比如Map
,List
等。
所以我们可以引入一个包装类来把数据包装下。
3. 代码分析
- 我们引入静态内部类,因为该类只是为
protostuff
序列化进行服务的,故使用静态内部类。 - 我们最好是在声明
SerializeDeserializeWrapper<T>
时,保存T
的引用,以便后续逻辑中保存包装的类型
//静态内部类
public static class SerializeDeserializeWrapper<T> {
//泛型的使用
private T data;
//建造者模式(返回实体类型)
public static <T> SerializeDeserializeWrapper<T> builder(T data) {
SerializeDeserializeWrapper<T> wrapper = new SerializeDeserializeWrapper<T>();
wrapper.setData(data);
return wrapper;
}
public void setData(T data) {
this.data = data;
}
public T getData() {
return data;
}
}
3.1 不使用泛型&&使用泛型
1.1 反序列化不使用泛型:
public static Object deserialize(byte[] data, Class clazz) {
try {
//判断是否是不可序列化对象,若是不能序列化对象,将对象进行包装
if (WRAPPER_SET.contains(clazz)) {
//SerializeDeserializeWrapper<T> wrapper = SerializeDeserializeWrapper.builder(clazz.newInstance());
SerializeDeserializeWrapper wrapper = new SerializeDeserializeWrapper();
ProtostuffIOUtil.mergeFrom(data, wrapper, WRAPPER_SCHEMA);
return wrapper.getData();
} else {
Object message = clazz.newInstance();
Schema<Object> schema = getSchema(clazz);
ProtostuffIOUtil.mergeFrom(data, message, schema);
return message;
}
} catch (Exception e) {
logger.error("反序列化对象异常 [" + clazz.getName() + "]", e);
throw new IllegalStateException(e.getMessage(), e);
}
}
1.2 测试方法
Map data = (HashMap) MyProtostuffUtils.deserialize(serializer, map.getClass());
2.1 反序列化使用泛型:
public static <T> T deserialize(byte[] data, Class<T> clazz) {
try {
//判断是否是不可序列化对象,若是不能序列化对象,将对象进行包装
if (WRAPPER_SET.contains(clazz)) {
//SerializeDeserializeWrapper<T> wrapper = SerializeDeserializeWrapper.builder(clazz.newInstance());
SerializeDeserializeWrapper<T> wrapper = new SerializeDeserializeWrapper<>();
ProtostuffIOUtil.mergeFrom(data, wrapper, WRAPPER_SCHEMA);
return wrapper.getData();
} else {
T message = clazz.newInstance();
Schema<T> schema = getSchema(clazz);
ProtostuffIOUtil.mergeFrom(data, message, schema);
return message;
}
} catch (Exception e) {
logger.error("反序列化对象异常 [" + clazz.getName() + "]", e);
throw new IllegalStateException(e.getMessage(), e);
}
}
2.2 测试方法:
String data = MyProtostuffUtils.deserialize(serializer, str.getClass());
3. 对比
两个都可执行成功,但是使用泛型
的话,
-
编译器
就可以进行类型检查
,注意在运行期会进行泛型擦除
,会找到用来替换类型参数
的具体类。例如SerializeDeserializeWrapper<String>
对象,运行期泛型擦除
后class
文件中的T
都会转化为String
类型;SerializeDeserializeWrapper
对象,运行期class
文件中的T
将会被转化为Object
类型。 - 因为返回值是泛型
T
,那么无需进行类型转换。 -
Class<T>
传入的class
对象后,我们就可以拿到T
的也就是class
对象的类型。这个传入的T
,可以在代码中任意地方使用。
3.2 源码分析
1. 源码分析:
- 将需要保存的信息保存到
Set<Class<?>>
里面,在类初始化的时候,使用static
进行加载。将需要包装的类型,保存到集合里面。 - 泛型通配符中
<?>
代表的是未知类型。可以接受Class<XXX>
所有的类型。注意:Class<Object>
不能接收Class<String>
等类型,因为他们不是父子类的关系。 - 需要一个
Cache
保存Class<?>
和Schema<?>
的关系,故可以使用ConcurrentHashMap
,保存映射关系。 - 因为使用的包装类型
SerializeDeserializeWrapper
,那么他们的class
类型和Schema
都是固定的。 - 用户的参数是泛型
T-Type
(而不是Object
)对象,那么便可以获取到对应的class
对象以及schema
对象。
2. 源码如下
package com.protoType;
import io.protostuff.LinkedBuffer;
import io.protostuff.ProtostuffIOUtil;
import io.protostuff.Schema;
import io.protostuff.runtime.RuntimeSchema;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
/***
* CACHE_SCHEMA 缓冲区。K-V 分别是class(类结构信息)对象和Schema(模板)对象
*
*/
public class MyProtostuffUtils {
private static Logger logger = LoggerFactory.getLogger(MyProtostuffUtils.class);
//将数据封装
private static final Set<Class<?>> WRAPPER_SET = new HashSet<>();
//包装类的Class对象
private static final Class<SerializeDeserializeWrapper> WRAPPER_CLASS = SerializeDeserializeWrapper.class;
//包装类的Schema对象
private static final Schema<SerializeDeserializeWrapper> WRAPPER_SCHEMA = RuntimeSchema.createFrom(WRAPPER_CLASS);
//安全缓存区,class对象和Schema对象
private static final Map<Class<?>, Schema<?>> CACHE_SCHEMA = new ConcurrentHashMap<>();
static {
WRAPPER_SET.add(List.class);
WRAPPER_SET.add(ArrayList.class);
WRAPPER_SET.add(CopyOnWriteArrayList.class);
WRAPPER_SET.add(LinkedList.class);
WRAPPER_SET.add(Stack.class);
WRAPPER_SET.add(Vector.class);
WRAPPER_SET.add(Map.class);
WRAPPER_SET.add(HashMap.class);
WRAPPER_SET.add(TreeMap.class);
WRAPPER_SET.add(LinkedHashMap.class);
WRAPPER_SET.add(Hashtable.class);
WRAPPER_SET.add(SortedMap.class);
// WRAPPER_SET.add(Object.class);
}
//注册需要使用包装类进行序列化的Class对象
public static void registerWrapperClass(Class clazz) {
WRAPPER_SET.add(clazz);
}
//获取序列化对象类型的schema
private static <T> Schema<T> getSchema(Class<T> clazz) {
Schema<T> schema = (Schema<T>) CACHE_SCHEMA.get(clazz);
if (schema == null) {
schema = RuntimeSchema.createFrom(clazz);
CACHE_SCHEMA.put(clazz, schema);
}
return schema;
}
//序列化对象
public static <T> byte[] serializer(T obj) {
//获取序列化对象
Class<T> clazz = (Class<T>) obj.getClass();
//设置缓数组缓冲区
LinkedBuffer buffer = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE);
byte[] bytes = null;
try {
Object serializerObj = obj; //获取序列化对象
Schema schema = WRAPPER_SCHEMA; //获取Schema对象
//包装class对象
if (WRAPPER_SET.contains(clazz)) {
//外部类是否可以使用静态内部类的成员?【外部类使用内部类的成员,需要新建内部类实例。】
serializerObj = SerializeDeserializeWrapper.builder(obj);//将class对象进行包装
} else {
//将class对象和schema对象保存到hashMap中
schema = getSchema(clazz); //获取Schema对象
}
//将对象转换为字节流
bytes = ProtostuffIOUtil.toByteArray(serializerObj, schema, buffer);
} catch (Exception e) {
logger.info("序列化{}失败", obj, e);
throw new IllegalStateException(e.getMessage());
} finally {
//回收buffer
buffer.clear();
}
return bytes;
}
public static <T> T deserialize(byte[] data, Class<T> clazz) {
try {
//判断是否是不可序列化对象,若是不能序列化对象,将对象进行包装
if (WRAPPER_SET.contains(clazz)) {
//SerializeDeserializeWrapper<T> wrapper = SerializeDeserializeWrapper.builder(clazz.newInstance());
SerializeDeserializeWrapper<T> wrapper = new SerializeDeserializeWrapper<>();
ProtostuffIOUtil.mergeFrom(data, wrapper, WRAPPER_SCHEMA);
return wrapper.getData();
} else {
T message = clazz.newInstance();
Schema<T> schema = getSchema(clazz);
ProtostuffIOUtil.mergeFrom(data, message, schema);
return message;
}
} catch (Exception e) {
logger.error("反序列化对象异常 [" + clazz.getName() + "]", e);
throw new IllegalStateException(e.getMessage(), e);
}
}
//静态内部类
public static class SerializeDeserializeWrapper<T> {
//泛型的使用
private T data;
//建造者模式(返回实体类型)
public static <T> SerializeDeserializeWrapper<T> builder(T data) {
SerializeDeserializeWrapper<T> wrapper = new SerializeDeserializeWrapper<T>();
wrapper.setData(data);
return wrapper;
}
public void setData(T data) {
this.data = data;
}
public T getData() {
return data;
}
}
}