文章目录
- 序列化是什么?
- 常见的序列化协议
- 使用
- 序列化
- 反序列化
- 序列化List
- 反序列化List
- 查看源码,分析不足
- 进行改善
序列化是什么?
如果我们需要持久化 Java 对象比如将 Java 对象保存在文件中,或者在网络传输 Java 对象,这些场景都需要用到序列化。
- 序列化:将数据结构或对象转换成可以存储或传输的形式,通常是二进制字节流,也可以是 JSON, XML 等文本格式
- 反序列化:将在序列化过程中所生成的数据转换为原始数据结构或者对象的过程
对于 Java 这种面向对象编程语言来说,我们序列化的都是对象(Object)也就是实例化后的类(Class)
下面是序列化和反序列化常见应用场景:
- 对象在进行网络传输(比如远程方法调用 RPC 的时候)之前需要先被序列化,接收到序列化的对象之后需要再进行反序列化;
- 将对象存储到文件之前需要进行序列化,将对象从文件中读取出来需要进行反序列化;
- 将对象存储到数据库(如 Redis)之前需要用到序列化,将对象从缓存数据库中读取出来需要反序列化;
- 将对象存储到内存之前需要进行序列化,从内存中读取出来之后需要进行反序列化。
序列化的主要目的是通过网络传输对象或者说是将对象存储到文件系统、数据库、内存中。
常见的序列化协议
JDK 自带的序列化方式一般不会用 ,因为序列化效率低并且存在安全问题。比较常用的序列化协议有 fastjson、jackson、protobuf(PB)、、、
在我的项目中,使用jackson
使用
我们所有的jackson序列化以及反序列化都是基于ObjecMapper来操作的
我们先定义一个错误的异常类。后续所有的序列化反序列化操作都是针对这个错误类实例出的对象而完成的。
@Data
public class CommonResult<T> implements Serializable {
private Integer code;
private T data;
private String msg;
public static <T> CommonResult<T> success(T data) {
CommonResult<T> result = new CommonResult<T>();
result.code = GlobalErrorCodeConstants.SUCCESS.getCode();
result.data = data;
result.msg = "";
return result;
}
public static <T> CommonResult<T> error(Integer code, String msg) {
Assert.isTrue(!GlobalErrorCodeConstants.SUCCESS.getCode().equals(code),
"code 不是错误的异常");
CommonResult<T> result = new CommonResult<T>();
result.code = code;
result.msg = msg;
return result;
}
public static <T> CommonResult<T> error(ErrorCode errorCode){
return error(errorCode.getCode(), errorCode.getMsg());
}
}
序列化
void TestJackson(){
ObjectMapper objectMapper = new ObjectMapper();
CommonResult<String> commonResult1 = CommonResult.error(500,"系统错误!");
//序列化
String str;
try {
str = objectMapper.writeValueAsString(commonResult1);
System.out.println(str);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}
反序列化
//反序列化
try {
CommonResult<String> commonResult = objectMapper.readValue(str, CommonResult.class);
System.out.println(commonResult.getCode()+commonResult.getMsg());
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
序列化List
//序列化list
List<CommonResult<String>> list = Arrays.asList(
CommonResult.success("success111"),
CommonResult.success("success222")
);
try {
str = objectMapper.writeValueAsString(list);
System.out.println(str);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
反序列化List
这里反序列化有一个注意事项,
objecMapper.readValue
里面的第二个参数 valueType不能直接放里不能直接放 list<CommonResult<String>>.class
,要使用 JavaType的方法 将list 和 list里面的元素 整合成一个类型
//反序列化list
JavaType listType = objectMapper.getTypeFactory().constructParametricType(
List.class,CommonResult.class
);
try {
list = objectMapper.readValue(str,listType);
System.out.println(list);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
运行结果如下
查看源码,分析不足
在我上述给出的代码中,我们可以看到,每一次进行序列化反序列化操作时,都需要进行try-catch操作,那么我们观察springboot的源码,可以来借鉴改善我们的代码
点开SpringBoot源码,找到反序列化List的方法。
public List<Object> parseList(String json) {
return (List)this.tryParse(() -> {
return (List)this.getObjectMapper().readValue(json, LIST_TYPE);
}, Exception.class);
}
我们观察到,里面调用到了tryParse
这个方法,我们点开这个方法
protected final <T> T tryParse(Callable<T> parser, Class<? extends Exception> check) {
try {
return parser.call();
} catch (Exception var4) {
Exception ex = var4;
if (check.isAssignableFrom(ex.getClass())) {
throw new JsonParseException(ex);
} else {
ReflectionUtils.rethrowRuntimeException(ex);
throw new IllegalStateException(ex);
}
}
}
我们可以看到parseList
中通过lambda表达式的方式,将异常全部在tryParse
中进行try-catch。
解析tryParse
- 参数列表
Callable<T> parser, Class<? extends Exception> check
第一个参数是一个Callable,第二个参数是一个Exception的class - 返回值
泛型T parser.call()
相当于是将parseList中的lambda表达式进行调用。(List)this.tryParse(() -> { return (List)this.getObjectMapper().readValue(json, LIST_TYPE);
catch (Exception var4) { Exception ex = var4; if (check.isAssignableFrom(ex.getClass())) { throw new JsonParseException(ex); }
检查捕获的这个var4异常是否是我们传入的check异常的实例或者是其子类的一个实例,如果是,就抛出JsonParseException(ex)
5.else { ReflectionUtils.rethrowRuntimeException(ex); throw new IllegalStateException(ex); }
不是预期的异常,就抛出IllegalStateException(ex)
进行改善
- 对ObjecMappper使用一个单例的构造方法
- 使用tryParse来对try-catch进行优化
public class JacksonUtil {
public JacksonUtil() {
}
//单例模式 创建objecMapper
private final static ObjectMapper OBJECT_MAPPER;
static {
OBJECT_MAPPER = new ObjectMapper();
}
private static ObjectMapper getObjectMapper() {
return OBJECT_MAPPER;
}
// 以后就可以直接调用这个方法 而不用再传一个异常
private static <T> T tryParse(Callable<T> parser) {
return tryParse(parser, JacksonException.class);
}
/*/
参考的是 springboot源码
*/
private static <T> T tryParse(Callable<T> parser,Class<? extends Exception> check) {
try {
return parser.call();
} catch (Exception ex) {
if (check.isAssignableFrom(ex.getClass())) {
throw new JsonParseException(ex);
}
throw new IllegalStateException(ex);
}
}
/**
* 序列化方法
* @param object
* @return
*/
public static String writeValueAsString(Object object){
return JacksonUtil.tryParse(()->{
return JacksonUtil.getObjectMapper().writeValueAsString(object);
});
}
/**
* 反序列化
* @param content
* @param valueType
* @return
* @param <T>
*/
public static<T> T readValue(String content, Class<T> valueType) {
return JacksonUtil.tryParse(()->{
return JacksonUtil.getObjectMapper().readValue(content, valueType);
});
}
/**
* 反序列化list
* @param content
* @param paramClasses
* @return
* @param <T>
*/
public static <T> T readListValue(String content, Class<?> paramClasses) {
JavaType javaType = JacksonUtil.getObjectMapper().getTypeFactory().constructParametricType(
List.class, paramClasses
);
return JacksonUtil.tryParse(()->{
return JacksonUtil.getObjectMapper().readValue(content, javaType);
});
}
}