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();
}
}