FST
快速序列化和反序列化
介绍
FST的使用有两种方式,一种是快捷方式,另一种需要使用ObjectOutput和ObjectInput。
快捷方式
static void quickStart() {
FSTConfiguration conf = FSTConfiguration.createAndroidDefaultConfiguration();
FstObject object = new FstObject();
object.setName("huaijin");
object.setAge(30);
System.out.println("serialization, " + object);
byte[] bytes = conf.asByteArray(object);
FstObject newObject = (FstObject) conf.asObject(bytes);
System.out.println("deSerialization, " + newObject);
}
直接使用FSTConfiguration提供的序列化和反序列化接口。FSTConfiguration也提供了注册对象的Class接口,如果不注册,默认会将对象的Class Name写入。
这个提供了易用高效的API方式,不使用ByteArrayOutputStreams而直接得到byte[]。
ObjectOutput和ObjectInput
static FSTConfiguration conf = FSTConfiguration.createAndroidDefaultConfiguration();
static void writeObject(OutputStream outputStream, FstObject fstObject) throws IOException {
FSTObjectOutput out = conf.getObjectOutput(outputStream);
out.writeObject(fstObject);
out.close();
}
static FstObject readObject(InputStream inputStream) throws Exception {
FSTObjectInput input = conf.getObjectInput(inputStream);
FstObject fstObject = (FstObject) input.readObject(FstObject.class);
input.close();
return fstObject;
}
能更细腻控制序列化的写入写出
配置
pom
<!-- 用于序列化和反序列化-->
<dependency>
<groupId>de.ruedigermoeller</groupId>
<artifactId>fst</artifactId>
<version>2.57</version>
</dependency>
FSTSerializer-fst单例
import org.nustaq.serialization.FSTConfiguration;
import org.nustaq.serialization.FSTDecoder;
import org.nustaq.serialization.FSTEncoder;
import org.nustaq.serialization.coders.FSTStreamDecoder;
import org.nustaq.serialization.coders.FSTStreamEncoder;
import java.io.IOException;
import java.lang.reflect.Field;
/**
* 使用fts进行序列化
*/
public class FSTSerializer {
static class FSTDefaultStreamCoderFactory implements FSTConfiguration.StreamCoderFactory {
// 单例的生成工厂,
// 并且让FSTStreamDecoder.chBufS FSTStreamDecoder.ascStringCache为空
Field chBufField;
Field ascStringCacheField;
{
try {
chBufField = FSTStreamDecoder.class.getDeclaredField("chBufS");
ascStringCacheField = FSTStreamDecoder.class.getDeclaredField("ascStringCache");
} catch (Exception e) {
throw new IllegalStateException(e);
}
ascStringCacheField.setAccessible(true);
chBufField.setAccessible(true);
}
private FSTConfiguration fstConfiguration;
FSTDefaultStreamCoderFactory(FSTConfiguration fstConfiguration) {
this.fstConfiguration = fstConfiguration;
}
@Override
public FSTEncoder createStreamEncoder() {
return new FSTStreamEncoder(fstConfiguration);
}
@Override
public FSTDecoder createStreamDecoder() {
return new FSTStreamDecoder(fstConfiguration) {
@Override
public String readStringUTF() throws IOException {
try {
String res = super.readStringUTF();
chBufField.set(this, null);// xField.set(FSTStreamDecoder obj, Object value) 把FSTStreamDecoder对象的xField设置为value
return res;
} catch (Exception e) {
throw new IOException(e);
}
}
@Override
public String readStringAsc() throws IOException {
try {
String res = super.readStringAsc();
ascStringCacheField.set(this, null);
return res;
} catch (Exception e) {
throw new IOException(e);
}
}
};
}
static ThreadLocal input = new ThreadLocal();
static ThreadLocal output = new ThreadLocal();
@Override
public ThreadLocal getInput() {
return input;
}
@Override
public ThreadLocal getOutput() {
return output;
}
}
private static class InstanceHolder { // 生成单例
private static final FSTConfiguration INSTANCE = FSTConfiguration.createDefaultConfiguration();
static {
INSTANCE.setStreamCoderFactory(new FSTDefaultStreamCoderFactory(INSTANCE));
}
}
public FSTConfiguration getConfig() {// 返回单例
return InstanceHolder.INSTANCE;
}
}
为什么解码之前要清空缓存?
装载: Fst反序列化失败_weixin_33708432的博客-CSDN博客
fst在做反序列化时 由于该数据原先序列化的时候类(可以称之为模板)是有10个字段,但是当开发增加了2个字段后 由于既存的数据中已经均是10个字段的数据(实例)但是事实上新的版本中已经变成12个字段了。
当反序列化时由于根据对应的class信息(没有指定version annotation)导致反序列化报错。
要求必须指定version,如果有需要设置默认值等等可以使用VersionConflictListener那么解决方案也很简单,当增加字段的时候直接将缓存清空即可(避免增加version字段)
FstRedisSerializer-redis使用fst单例
import com.yami.shop.common.serializer.FSTSerializer;
import lombok.SneakyThrows;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.lang.Nullable;
/**
* 使用fst 进行reids的序列化
*/
public class FstRedisSerializer implements RedisSerializer<Object> {
private static final byte[] EMPTY_ARRAY = new byte[0];
@Override
@SneakyThrows
public byte[] serialize(Object o) {
if (o == null) {
return EMPTY_ARRAY;
}
return new FSTSerializer().getConfig().asByteArray(o);
}
@Override
@SneakyThrows
public Object deserialize(byte[] bytes) {
if (isEmpty(bytes)) {
return null;
}
return new FSTSerializer().getConfig().asObject(bytes);
}
private static boolean isEmpty(@Nullable byte[] data) {
return (data == null || data.length == 0);
}
}
FstCodec-redisson使用fst单例
import org.nustaq.serialization.FSTConfiguration;
import org.nustaq.serialization.FSTObjectInput;
import org.nustaq.serialization.FSTObjectOutput;
import org.redisson.client.codec.BaseCodec;
import org.redisson.client.handler.State;
import org.redisson.client.protocol.Decoder;
import org.redisson.client.protocol.Encoder;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator;
import io.netty.buffer.ByteBufInputStream;
import io.netty.buffer.ByteBufOutputStream;
/**
* 被redisson使用
* Efficient and speedy serialization codec fully
* compatible with JDK Serialization codec.
*
* https://github.com/RuedigerMoeller/fast-serialization
*
* @author Nikita Koksharov
*
*/
public class FstCodec extends BaseCodec {
private final FSTConfiguration config;
public FstCodec() {
config = new FSTSerializer().getConfig();
}
private final Decoder<Object> decoder = new Decoder<Object>() {
@Override
public Object decode(ByteBuf buf, State state) throws IOException {
ByteBufInputStream in = new ByteBufInputStream(buf);
FSTObjectInput inputStream = config.getObjectInput(in);
try {
return inputStream.readObject();
} catch (IOException e) {
throw e;
} catch (Exception e) {
throw new IOException(e);
}
}
};
private final Encoder encoder = new Encoder() {
@Override
public ByteBuf encode(Object in) throws IOException {
ByteBuf out = ByteBufAllocator.DEFAULT.buffer();
ByteBufOutputStream os = new ByteBufOutputStream(out);
FSTObjectOutput oos = config.getObjectOutput(os);
try {
oos.writeObject(in);
oos.flush();
return os.buffer();
} catch (IOException e) {
out.release();
throw e;
} catch (Exception e) {
out.release();
throw new IOException(e);
}
}
};
@Override
public Decoder<Object> getValueDecoder() {
return decoder;
}
@Override
public Encoder getValueEncoder() {
return encoder;
}
@Override
public ClassLoader getClassLoader() {
if (config.getClassLoader() != null) {
return config.getClassLoader();
}
return super.getClassLoader();
}
}