泛型學(xué)習(xí)目錄:
Java泛型-1(泛型的定義)
Java泛型-2(通配符)
Java泛型-3(實(shí)踐篇-protostuff序列化與反序列化)
Java泛型-4(類型擦除后如何獲取泛型參數(shù))
2. protostuff的準(zhǔn)備工作
Java有一個(gè)序列化的技術(shù),就是把Object轉(zhuǎn)換為可保存,可傳輸?shù)牧鲾?shù)據(jù)齐饮。
而Protostuff
就是一個(gè)優(yōu)秀的序列化框架爹梁。
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
的時(shí)候會遇到一些無法序列化/反序列化的對象丧鸯,比如Map
杨凑,List
等览徒。
所以我們可以引入一個(gè)包裝類來把數(shù)據(jù)包裝下突诬。
3. 代碼分析
- 我們引入靜態(tài)內(nèi)部類,因?yàn)樵擃愔皇菫?code>protostuff序列化進(jìn)行服務(wù)的睦袖,故使用靜態(tài)內(nèi)部類珊肃。
- 我們最好是在聲明
SerializeDeserializeWrapper<T>
時(shí),保存T
的引用,以便后續(xù)邏輯中保存包裝的類型
//靜態(tài)內(nèi)部類
public static class SerializeDeserializeWrapper<T> {
//泛型的使用
private T data;
//建造者模式(返回實(shí)體類型)
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 {
//判斷是否是不可序列化對象近范,若是不能序列化對象嘶摊,將對象進(jìn)行包裝
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 {
//判斷是否是不可序列化對象,若是不能序列化對象评矩,將對象進(jìn)行包裝
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. 對比
兩個(gè)都可執(zhí)行成功叶堆,但是使用泛型
的話,
-
編譯器
就可以進(jìn)行類型檢查
斥杜,注意在運(yùn)行期會進(jìn)行泛型擦除
虱颗,會找到用來替換類型參數(shù)
的具體類。例如SerializeDeserializeWrapper<String>
對象蔗喂,運(yùn)行期泛型擦除
后class
文件中的T
都會轉(zhuǎn)化為String
類型忘渔;SerializeDeserializeWrapper
對象,運(yùn)行期class
文件中的T
將會被轉(zhuǎn)化為Object
類型缰儿。 - 因?yàn)榉祷刂凳欠盒?code>T畦粮,那么無需進(jìn)行類型轉(zhuǎn)換。
-
Class<T>
傳入的class
對象后乖阵,我們就可以拿到T
的也就是class
對象的類型宣赔。這個(gè)傳入的T
,可以在代碼中任意地方使用瞪浸。
3.2 源碼分析
1. 源碼分析:
- 將需要保存的信息保存到
Set<Class<?>>
里面儒将,在類初始化的時(shí)候,使用static
進(jìn)行加載对蒲。將需要包裝的類型钩蚊,保存到集合里面。 - 泛型通配符中
<?>
代表的是未知類型蹈矮∨槁撸可以接受Class<XXX>
所有的類型。注意:Class<Object>
不能接收Class<String>
等類型含滴,因?yàn)樗麄儾皇歉缸宇惖年P(guān)系诱渤。 - 需要一個(gè)
Cache
保存Class<?>
和Schema<?>
的關(guān)系丐巫,故可以使用ConcurrentHashMap
谈况,保存映射關(guān)系。 - 因?yàn)槭褂玫陌b類型
SerializeDeserializeWrapper
递胧,那么他們的class
類型和Schema
都是固定的碑韵。 - 用戶的參數(shù)是泛型
T-Type
(而不是Object
)對象,那么便可以獲取到對應(yīng)的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 緩沖區(qū)祝闻。K-V 分別是class(類結(jié)構(gòu)信息)對象和Schema(模板)對象
*
*/
public class MyProtostuffUtils {
private static Logger logger = LoggerFactory.getLogger(MyProtostuffUtils.class);
//將數(shù)據(jù)封裝
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);
//安全緩存區(qū),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);
}
//注冊需要使用包裝類進(jìn)行序列化的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();
//設(shè)置緩數(shù)組緩沖區(qū)
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)) {
//外部類是否可以使用靜態(tài)內(nèi)部類的成員?【外部類使用內(nèi)部類的成員联喘,需要新建內(nèi)部類實(shí)例华蜒。】
serializerObj = SerializeDeserializeWrapper.builder(obj);//將class對象進(jìn)行包裝
} else {
//將class對象和schema對象保存到hashMap中
schema = getSchema(clazz); //獲取Schema對象
}
//將對象轉(zhuǎn)換為字節(jié)流
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 {
//判斷是否是不可序列化對象豁遭,若是不能序列化對象叭喜,將對象進(jìn)行包裝
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);
}
}
//靜態(tài)內(nèi)部類
public static class SerializeDeserializeWrapper<T> {
//泛型的使用
private T data;
//建造者模式(返回實(shí)體類型)
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;
}
}
}