序列化
·
序列化 (Serialization)是将对象的状态信息转换为可以存储或传输的形式的过程。在序列化期间,对象将其当前状态写入到临时或持久性存储区。以后,可以通过从存储区中读取或反序列化对象的状态,重新创建该对象。
·序列化使其他代码可以查看或修改,那些不序列化便无法访问的对象实例数据。确切地说,代码执行序列化需要特殊的权限:即指定了 SerializationFormatter 标志的 SecurityPermission。在默认策略下,通过 Internet 下载的代码或 Internet 代码不会授予该权限;只有本地计算机上的代码才被授予该权限。
·通常,对象实例的所有字段都会被序列化,这意味着数据会被表示为实例的序列化数据。这样,能够解释该格式的代码有可能能够确定这些数据的值,而不依赖于该成员的可访问性。类似地,反序列化从序列化的表示形式中提取数据,并直接设置对象状态,这也与可访问性规则无关。
·对于任何可能包含重要的安全性数据的对象,如果可能,应该使该对象不可序列化。如果它必须为可序列化的,请尝试生成特定字段来保存不可序列化的重要数据。如果无法实现这一点,则应注意该数据会被公开给任何拥有序列化权限的代码,并确保不让任何恶意代码获得该权限。
内容摘自百科:序列化
简而言之:
- 序列化:将
对象
转换成字节流
的过程。 - 反序列化:将
字节流
转换成对象
的过程。
那么,什么情况才需要用到它呢?
- 因为ta的定义,我们可以想象的到
a、数据持久化
b、对象传输(进程间/网络中)
序列化方式
- Serializable Java内置序列化API
- Protobuf Google提供高效序列化API
-
Protostuff 开源社区基于
Protobuf
封装API
以上仅做例子,那么做出选择的动机是什么呢?
开发过程中,由上面的用途中,我们可以联想到:
- 时间上:尽可能快
- 空间上:尽可能小
- 操作上:尽可能便捷
由此可进行简单对比:
Serializable 相对速度慢、占空间、操作便捷(可序列化所有类)
Protobuf 相对速度快、空间占用小、操作繁琐(中间过程多)
Protostuff 相比速度快、空间小、操作便捷(隐藏Protobuf中间细节)
实际对比可参照 # [java]序列化框架性能对比(kryo、hessian、java、protostuff)
- 可以得出
Protostuff
占用小,速度快 -> 适合做数据存储或 RPC 数据交换格式 - 但又由于
Protostuff
序列化后,可读性差
- 生产环境优先选择
Protostuff
- 开发环境可选择
Serializable
(可读性对友方友好,易调戏/试)
实践
Maven
<!-- Redis Start -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
<!--
Redis序列化方式 : 采用高性能省内存空间的Google开源的序列化方式 Protostuff
-->
<dependency>
<groupId>io.protostuff</groupId>
<artifactId>protostuff-core</artifactId>
</dependency>
<dependency>
<groupId>io.protostuff</groupId>
<artifactId>protostuff-runtime</artifactId>
</dependency>
<!-- Redis End -->
Protostuff序列化工具类
public class ProtostuffSerializer implements RedisSerializer<Object> {
// Protostuff 无法直接序列化集合类对象,需要包装类包一下
private static class ProtoWrapper {
public Object data;
}
private final Schema<ProtoWrapper> schema;
private final ProtoWrapper wrapper;
private final LinkedBuffer buffer;
public ProtostuffSerializer() {
this.wrapper = new ProtoWrapper();
/* RuntimeSchema.getSchema(clazz);
实际上会将Schema<T>对象缓存 */
this.schema = RuntimeSchema.getSchema(ProtoWrapper.class);
this.buffer = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE);
}
/**
* 序列化方法,把指定对象序列化成字节数组
*
* @param t
* @return
* @throws SerializationException
*/
@Override
public byte[] serialize(Object t) throws SerializationException {
if (t == null) {
return new byte[0];
}
wrapper.data = t;
try {
return ProtostuffIOUtil.toByteArray(wrapper, schema, buffer);
} finally {
buffer.clear();
}
}
/**
* 反序列化方法,将字节数组反序列化成指定Class类型
*
* @param bytes
* @return
* @throws SerializationException
*/
@Override
public Object deserialize(byte[] bytes) throws SerializationException {
if (isEmpty(bytes)) {
return null;
}
ProtoWrapper newMessage = schema.newMessage();
ProtostuffIOUtil.mergeFrom(bytes, newMessage, schema);
return newMessage.data;
}
private boolean isEmpty(byte[] data) {
return (data == null || data.length == 0);
}
}
此时可以拿工具进行测验啦??
替换Redis序列化默认配置
@Configuration
public class RedisCacheConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(LettuceConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
factory.setDatabase(cacheDbIdx);
template.setConnectionFactory(factory);
// 使用ProtostuffSerializer 替换默认序列化
RedisSerializer serializer = new ProtostuffSerializer();
// 设置value的序列化规则和 key的序列化规则
template.setKeySerializer(serializer);
template.setValueSerializer(serializer);
template.setDefaultSerializer(serializer);
template.setHashKeySerializer(serializer);
template.setHashValueSerializer(serializer);
template.setStringSerializer(serializer);
template.afterPropertiesSet();
// template.setEnableTransactionSupport(true);
return template;
}
}
二级缓存类
@Slf4j
public class MplusRedisCache implements Cache {
private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
private String id;
public MplusRedisCache(String id) {
if (id == null) {
throw new IllegalArgumentException("Cache instances require an ID");
}
this.id = id;
log.debug("[创建了mybatis的redis缓存:{}]" + id);
}
private RedisTemplate<Object, Object> getRedisTemplate(){
return ApplicationContextHolder.getBean("redisTemplate");
}
@Override
public String getId() {
return id;
}
@Override
public void putObject(Object key, Object value) {
RedisTemplate redisTemplate = getRedisTemplate();
redisTemplate.boundHashOps(getId()).put(key, value);
log.info("[结果放入到缓存中: " + key + " = " + value+" ]");
}
@Override
public Object getObject(Object key) {
RedisTemplate redisTemplate = getRedisTemplate();
Object value = redisTemplate.boundHashOps(getId()).get(key);
log.info("[从缓存中获取了: " + key + " = " + value+" ]");
return value;
}
@Override
public Object removeObject(Object key) {
RedisTemplate redisTemplate = getRedisTemplate();
Object value = redisTemplate.boundHashOps(getId()).delete(key);
log.info("[从缓存删除了: " + key + "=" + value+" ]");
return value;
}
@Override
public void clear() {
RedisTemplate redisTemplate = getRedisTemplate();
redisTemplate.delete(getId());
log.info("[清空缓存!!!哭哭]");
}
@Override
public int getSize() {
RedisTemplate redisTemplate = getRedisTemplate();
Long size = redisTemplate.boundHashOps(getId()).size();
return size == null ? 0 : size.intValue();
}
@Override
public ReadWriteLock getReadWriteLock() {
return readWriteLock;
}
}
测试结果~
至此Protostuff简易使用完成啦~??