序
标题照着why哥 copy一份,哈哈,希望大佬来点好运气

你好呀,我是雨夜。
在 fury 的官网中看到一个 '判断是不是jdk序列化' 的方法。

看明白之后,我觉得还是有点意思的,结合自己的理解和代码,加上画几张图,给你拆解一下 fury 里面的"判断序列化类型"。
虽然是在 fury 里面看到的,但是本篇文章的内容和 fury 框架没有太多关系,反而和基础知识有关。
所以,即使你不了解 fury 框架,也不影响你阅读。
当你理解了这个类的工作原理之后,你完全可以把这个只有 10 多行的类搬运到你的项目里面,然后就变成你的了。
你懂我意思吧。
jdk 序列化
怎么判断的

vbnet
public static short getShortB(byte[] b, int off) {
return (short) ((b[off + 1] & 0xFF) + (b[off] << 8));
}
里面的具体 带入值为 (short) ((-19 & 0xFF) + (-84 << 8));
(short) ((237) + (-21504));
(short)-21267
然后和 0xaced 进行判断 是否相等,相等的话就认为 是 jdk序列化
jdk 序列化代码
ini
public ObjectOutputStream(OutputStream out) throws IOException {
verifySubclass();
bout = new BlockDataOutputStream(out);
handles = new HandleTable(10, (float) 3.00);
subs = new ReplaceTable(10, (float) 3.00);
enableOverride = false;
writeStreamHeader();
bout.setBlockDataMode(true);
if (extendedDebugInfo) {
debugInfoStack = new DebugTraceInfoStack();
} else {
debugInfoStack = null;
}
}
csharp
protected void writeStreamHeader() throws IOException {
bout.writeShort(STREAM_MAGIC);
bout.writeShort(STREAM_VERSION);
}
arduino
final static short STREAM_MAGIC = (short)0xaced;
往里面先写了一个 0xaced 作为标识,就是根据这个字段 判断是不是jdk序列化
为什么(short) ((-19 & 0xFF) + (-84 << 8)); 就能取出0xaced呢?
0xaced 十进制代表44269
进制转换结果为:1010001110101000
首先我们看下 jdk序列化的方法
java
public void writeShort(int v) throws IOException {
if (pos + 2 <= MAX_BLOCK_SIZE) {
Bits.putShort(buf, pos, (short) v);
pos += 2;
} else {
dout.writeShort(v);
}
}
v是 -21267,pos = 0,MAX_BLOCK_SIZE = 1024
scss
static void putShort(byte[] b, int off, short val) {
b[off + 1] = (byte) (val );
b[off ] = (byte) (val >>> 8);
}
带入值
scss
static void putShort(byte[] b, int off, short val) {
b[1] = (byte) (-21267 );
b[0] = (byte) (-21267 >>> 8);
}
所以我们取 b[1]就是 0xaced
0xaced是序列化的头信息字段。在Java的序列化机制中,当一个对象被序列化时,会先写入一个表示序列化信息的头,其中包含了魔术数字(magic number)和版本号。0xaced就是表示序列化的魔术数字,它表示序列化的格式和版本号。
停一下 ,你看到0xaced 还能想起什么?
停个几秒 想下

常用的BIOS中断有这些:
1、向屏幕输出:int 0x10
2、读写软盘:int 0x13
3、系统调用:int 0x80

性能比较
我第一次测试的结果
问了下大佬

但是后面具体看的时候,发现不是这块的事
这块我用的是 arthas
结果你告诉我这

执行过一次 方法之后 就可以了,因为里面用了 JIT 生成了一些类

fury 序列化消耗的时间 353
jdk 序列化消耗的时间 69
fury 序列化消耗的时间 3
jdk 序列化消耗的时间 23
fury 序列化消耗的时间 5
jdk 序列化消耗的时间 22
这个时候 已经卡住了,要想别的办法 确定是哪部分耗时了
我就把源码下载下来,直接添加了一个java agent 直接打印每一行的耗时
确定
ini
byte[] bytes = fury.serialize(object);
耗时比较多,它第一次执行消耗了100ms
scss
public Class<? extends Serializer> getSerializerClass(Class<?> cls) {
boolean codegen =
supportCodegenForJavaSerialization(cls) && fury.getConfig().isCodeGenEnabled();
return getSerializerClass(cls, codegen);
}
bash
io.fury.resolver.ClassResolver#addSerializer
真实JIT 生成代码的代码
swift
public static <T> Class<Serializer<T>> loadCodegenSerializer(Fury fury, Class<T> cls) {
try {
return (Class<Serializer<T>>) CodecUtils.loadOrGenObjectCodecClass(cls, fury);
} catch (Exception e) {
String msg = String.format("Create sequential serializer failed, \nclass: %s", cls);
throw new RuntimeException(msg, e);
}
}
JIT 生成的class 名字怎么获取的
scss
public String codecClassName(Class<?> beanClass) {
String name = ReflectionUtils.getClassNameWithoutPackage(beanClass).replace("$", "_");
StringBuilder nameBuilder = new StringBuilder(name);
if (fury.trackingRef()) {
// Generated classes are different when referenceTracking is switched.
// So we need to use a different name.
nameBuilder.append("FuryRef");
} else {
nameBuilder.append("Fury");
}
nameBuilder.append(codecSuffix()).append("Codec");
nameBuilder.append('_').append(fury.getConfig().getConfigHash());
String classUniqueId = CodeGenerator.getClassUniqueId(beanClass);
if (StringUtils.isNotBlank(classUniqueId)) {
nameBuilder.append('_').append(classUniqueId);
}
return nameBuilder.toString();
}
他是怎么让uuid 不重复的呢? 看TODO 他们 现在没有根据 configHash 区别,而是依靠 classUniqueId 字段保证的唯一,下面是一个例子
SomeClassFuryCodec_97705842_414493378_242131142
生成之后的 代码
ini
package io.fury.test;
import java.util.List;
import java.util.Map;
import java.util.Set;
import io.fury.Fury;
import io.fury.memory.MemoryBuffer;
import io.fury.resolver.NoRefResolver;
import io.fury.resolver.ClassInfo;
import io.fury.resolver.ClassInfoCache;
import io.fury.resolver.ClassResolver;
import io.fury.builder.Generated;
import io.fury.serializer.CodegenSerializer.LazyInitBeanSerializer;
import io.fury.serializer.Serializers.EnumSerializer;
import io.fury.serializer.Serializer;
import io.fury.serializer.StringSerializer;
import io.fury.serializer.ObjectSerializer;
import io.fury.serializer.CompatibleSerializer;
import io.fury.serializer.CollectionSerializers.CollectionSerializer;
import io.fury.serializer.MapSerializers.MapSerializer;
import io.fury.builder.Generated.GeneratedObjectSerializer;
public final class SomeClassFuryCodec_97705842_414493378_242131142 extends GeneratedObjectSerializer {
private final NoRefResolver refResolver;
private final ClassResolver classResolver;
private final StringSerializer strSerializer;
private final Fury fury;
public SomeClassFuryCodec_97705842_414493378_242131142(Fury fury, Class classType) {
super(fury, classType);
this.fury = fury;
fury.getClassResolver().setSerializerIfAbsent(classType, this);
io.fury.resolver.RefResolver refResolver0 = fury.getRefResolver();
refResolver = ((NoRefResolver)refResolver0);
classResolver = fury.getClassResolver();
strSerializer = fury.getStringSerializer();
}
@Override public final void write(MemoryBuffer buffer, Object obj) {
io.fury.test.SomeClass someClass20 = (io.fury.test.SomeClass)obj;
buffer.writeInt(-1300032068);
Object object1 = io.fury.util.Platform.getObject(someClass20, 12L);
String name = (String)object1;
if ((name == null)) {
buffer.writeByte(((byte)-3));
} else {
buffer.writeByte(((byte)0));
strSerializer.writeJava8StringCompressed(buffer, name);
}
}
@Override public final Object read(MemoryBuffer buffer) {
ObjectSerializer.checkClassVersion(fury, buffer.readInt(), -1300032068);
io.fury.test.SomeClass someClass3 = new io.fury.test.SomeClass();
refResolver.reference(someClass3);
if ((buffer.readByte() != ((byte)-3))) {
io.fury.util.Platform.putObject(someClass3, 12L, strSerializer.readJava8CompressedString(buffer));
} else {
io.fury.util.Platform.putObject(someClass3, 12L, null);
}
return someClass3;
}
}
使用的时候 肯定是 把beanClass 和 生成的新class 进行map 关联,然后序列化之前 看下map 是否存在,如果存在就用新的
小配置
如果你想关闭 代码生成的开关怎么办?
scss
Fury fury = Fury.builder().withLanguage(Language.JAVA)
.withCodegen(false)
// .withSecureMode(false)
.build();
其中 .withCodegen(false) true为生成代码,false是不生成,默认 true
这个时候 你可能发现withCodegen 赋值的是Fury.class 类中的属性,而最后判断的时候 是
scss
fury.getConfig().isCodeGenEnabled()
scss
Config(Fury.FuryBuilder builder) {
// 省略
codeGenEnabled = builder.codeGenEnabled;
// 省略
}
就是为了代码 结构的清晰
如果想判断类是不是 非静态内部类
typescript
public static boolean supportCodegenForJavaSerialization(Class<?> cls) {
// bean class can be static nested class, but can't be a non-static inner class
return cls.getEnclosingClass() == null || Modifier.isStatic(cls.getModifiers());
}
判断class 是不是jdk代理的对象
typescript
public static boolean isJdkProxy(Class<?> clz) {
return Proxy.isProxyClass(clz);
}
包名 java 开头的,不知道为什么报错
less
if (fury.getConfig().checkJdkClassSerializable()) {
if (cls.getName().startsWith("java") && !(Serializable.class.isAssignableFrom(cls))) {
throw new UnsupportedOperationException(
String.format("Class %s doesn't support serialization.", cls));
}
}
如果是A.isAssignableFrom(B)
确定一个类(B)是不是继承来自于另一个父类(A),一个接口(A)是不是实现了另外一个接口(B),或者两个类相同。主要,这里比较的维度不是实例对象,而是类本身,因为这个方法本身就是Class
类的方法,判断的肯定是和类信息相关的。
获取当前jdk 版本
ini
String property = System.getProperty("java.specification.version");
序列化的原理

io.fury.resolver.ClassResolver#createSerializer 方法是具体方法
彩蛋

我当时买的房子 群里有人2023年3月收到房子就装修,然后租了几个月,还要继续往外租
这种房子一定不要租,都是甲醛,他们从来不住,就为了多赚点房租,也不告诉人家
这里有的人可能要说了,'谁让你不问了,你问了我就告诉你了',靠,租房子,没有行情价么,还让人家问,人家刚看你房子一次,就知道有没有什么问题,你怎么不上天呢?
今天看到就来气,我往外租房子都是先告诉人家 问题在哪,能接受就接受,接受不了就算了,哈哈,但是我这个人不接受砍价,就是实实在在的,别搞事,我们先把不好的地方都说了,后面的沟通就好多了。
你告诉我,新房子采用好材料,可以没有甲醛的,我不否认,但是我不认为他会用好的材料,对别人好点,就是对自己好点
这也不是聪明人不聪明人,租房经验丰富的事,算了,做好自己