从设计者的角度出发理解源码--FastJson
引言
本篇,作为《从..理解源码》的第二篇,延续前一篇的思路,笔者视图尝试从作者的角度触发分析作者的思路历程,和上篇相比,FastJson的知识可能略多。这篇文章,对fastjson只能算是浅尝即止,我认为最厉害的未过于,作者的持续习惯,包括但不限于:
基于Benchmark搜集高性能代码的习惯
阅读原始文档获得第一手资料,对于JDK各种版本特性了然于胸。
喜欢探索代码优雅的编码方式。
产品思维:先发现问题,先用简单的方式实现,后面在迭代更好的方案。
自我高要求:把自己的代码拉出来被鞭尸,虚心接受建议,持续迭代,即便有其他框架在开发,即便自己已经是P9。
这个框架的阅读过程中,感受到作者的拼劲全力,感受到:一个极客,孜孜不倦,为当年立下的Flag,将自己空闲时间,都投入到少年时的一个梦:极致的性能。
写本篇文章时,由于fastjson2已发布,本篇文章基于fastjson2
为什么要写FastJson
哎,以往的框架,Jakson,Goson,最大的问题其实是使用繁琐,和在不同环境下,兼容性不足
我需要给别人一个足够的理由,做俩个选择
- 选择放弃原本的实用。
- 选择使用我的东西。
有了:
-
以性能为突破口
我从性能上出发,应该是一个比较好的突破口,因为一个逻辑一旦涉及到序列化操作,该序列化操作的频次应该不会低。
如果我可以真正做到序列化性能有比较大的提升,对他们来说很有吸引力。
-
上手体验为突破口:
1.使用API设计均使用静态方法思路,减少编码步骤,
2.提供充足的注解类工具,使用注解天生体验好的特性。
框架 写法 上手代码量 Fastjson String mapString = JSON.toJSONString(map); 1 行 Jackson ObjectMapper mapper = new ObjectMapper();<br />String beanString = mapper.toJson(map); 2 行 Gson Gson gson = new Gson();<br />String beanString = gson.toJson(map); 2行 FastJSON提供的注解如下:
// 字段注解 com.alibaba.fastjson2.annotation.JSONField com.alibaba.fastjson2.annotation.JSONType // com.alibaba.fastjson2.annotation.JSONBuilder com.alibaba.fastjson2.annotation.JSONCompiled com.alibaba.fastjson2.annotation.JSONCompiler
先在公司内部(阿里)获得一定的认可度,后面推广可以借助公司的力量。
其实,做框架设计起初和写PPT有点类似,你得先有一个能引人入胜的思路,最好能有个最简陋的版本,先将人吸引过来,然后你逐步去完善它,持之以恒的迭代,再产生新的思路,再迭代。
哎,光能证明你性能好也不行呀,稳定性怎么保证,市面上相似的太多了。
有了:
- 持续关注issues及时修复,把每个issues的修复单元测试及时提交修改完成之后,让他们随意查看,逐步建立起信任。
- 保持初心,持之以恒,一个框架能被大面积使用,不在于它开始bug的多少,而在于立意远大,且有人维护。
- 这个东西如果做好了,是能反向推动我成长的,因为它的适用范围足够广(安卓的,Web,大数据等等)
序列化设计(Bean->Json)
序列化流程,如果再细分的话,有如下流程:
获取所有需要提取的属性列表。
依次遍历属性列表获取属性,向一段JSON容器里面放置key,val。
在放置key,val的时候,可能涉及某一些转义符的处理,json结构的处理等等。
不同场景的优化
哎,我如何基于不同情况做出不同的处理方式呢,这样我可以针对每一种情况去做具体的优化
有了:
- 我可以使用模板方法写一个ObjectWriter抽象类完成主逻辑。
- 将已知情况细分清楚,基于不同情况,各自情况完成各自的实现。
// ObjectWriter抽象类,以及部分具体实现
com.alibaba.fastjson2.writer.ObjectWriter
com.alibaba.fastjson2.util.JodaSupport.LocalDateTimeWriter
com.alibaba.fastjson2.util.GuavaSupport.AsMapWriter
....
哎,总不能把所有情况都穷举出来,写出各自的实现,这种优化思路还是不太灵活。
有了:Bean转JSON,本质上,就是从一个Bean里面取内容,然后往一个JSON里面塞么。
我可以为每一个Bean的,读写逻辑,分别依据各自的情况生成相应的字节码读写器。
- 依据每一个Bean的情况去做更具体的优化。
- 后期扩展空间也大(毕竟是动态生成的代码)。
- 需要通过一个抽象类来约束核心逻辑,使用ASM框架运行时编写Class。
// 如下摘自:com.alibaba.fastjson2.JSON#toJSONString(java.lang.Object),部分逻辑省略
//1:获取ObjectWriter
com.alibaba.fastjson2.writer.ObjectWriterProvider#getObjectWriter
//1.1:使用 ASM 来创建 ObjectWriter 对象
1.1 creator = ObjectWriterCreatorASM.INSTANCE; //
//1.2:通过字节码编写代码,继承ObjectWriterAdapter,创建Class。
1.2 com.alibaba.fastjson2.reader.ObjectWriterCreatorASM#createObjectWriter
//2:完成读序列化
objectWriter.write(writer, object, null, null, 0);
//3:返回
return writer.toString();
ASM生成的代码
如下我列出了FastJSON生成的ObjectReaderAdapter:
FastJSON生成的ObjectReaderAdapter: https://note.youdao.com/s/AfCaGZSV
FastJSON生成的ObjectWriterAdapter: https://note.youdao.com/s/W9gi9fRD
不能使用ASM的环境
哎,可能部分使用者的场景不能使用字节码,可能由于环境问题,比如Android,GraalVM环境等,或者出于安全上面考量,使用者主观不想用字节码实现
有了:
- 支持让使用者指定使用的实现方式。
- 默认使用ASM字节码实现,如果发现是安卓等不能够使用的环境,自动使用反射实现,且支持手动指定。
// 核心源码如下: 1.优先支持手动指定方式,2.如果不指定,默认使用ASM方式,3.如果当前环境不支持ASM,则自动转为MH,或者反射。
switch (JSONFactory.CREATOR) {
case "reflect":
case "lambda":
creator = ObjectWriterCreator.INSTANCE;
break;
case "asm":
default:
try {
if (!JDKUtils.ANDROID && !JDKUtils.GRAAL) {
creator = ObjectWriterCreatorASM.INSTANCE;
}
} catch (Throwable ignored) {}
if (creator == null) {
// 如果 creator 仍然为 null,则使用反射或 Lambda 表达式来创建 ObjectWriter 对象
creator = ObjectWriterCreator.INSTANCE;
}
break;
}
哎,反射好像性能不太好,我如何在这个基础之上优化一点呢?
有了:
- 我可以使用:Lambda表达式+MethodHandler,来替代反射获取属性的调用。
- JDK自带了很多FunctionInterface接口比如Customer ,这样我不仅可以提升性能,还可以在调用处统一写法。
invokedynamic指令与MethodHandle的功能简单说:就是之前的动态转发的逻辑都是在字节码层面去完成的,某个方法调用,转发给哪个具体实现类去执行,都是字节码自己安排好了,基于一段固定逻辑自己走下来
而invokedynamic指令与MethodHandle则可以将“转发给谁”,这个谁,在应用代码层面去指定。
Lambda表达式主要就是基于如下几点
1.通过ASM字节码技术,生成最轻量代码(比如static方法)
2.基于指令invokedynamic与MethodHandle特性实现的,将转发能力赋予了业务代码。
// P1:创建set函数的Lambda对象
BiConsumer function = (BiConsumer) lambdaSetter(objectClass, fieldClass, method);
return createFieldReader(objectClass, objectType, fieldName, fieldType, fieldClass, ordinal, features, format, locale, defaultValue, jsonSchema, method, function, initReader);
// P2:将set函数的Lambda对象放到缓存fieldReaders中
putIfAbsent(fieldReaders, fieldName, fieldReader, objectClass);
// P3:使用function读取值
public void readFieldValue(JSONReader jsonReader, T object) {
function.accept(object, (V) fieldValue);
}
知识补习:
安全性方面
哎,如何防止JSON注入呢?毕竟用户输入什么我又不可控制?
有了:
-
Json-schema不就就是干这个的,我可以用Json-schema约束输入值,防止被注入。
Json-schema是一个特殊的JSON,用来约束JSON的数据结构。
-
使用模板方法设计模式,实现一个基础模板类,基于基础类型(int,long,string,object)来实现具体的验证逻辑(用户通过注解的形式作用在某个具体字段上)
-- 基础模板
com.alibaba.fastjson2.schema.JSONSchema
-- 核心方法(字段验证)
com.alibaba.fastjson2.schema.JSONSchema#validate
-- 具体类型实现
com.alibaba.fastjson2.schema.BooleanSchema
com.alibaba.fastjson2.schema.ObjectSchema
-- 组合规则
com.alibaba.fastjson2.schema.AnyOf
com.alibaba.fastjson2.schema.AllOf
其中,object,组合类(AnyOf,AllOf)方法可能有一些特殊实现,因为object类型需要考虑递归的情况。组合类的需要考虑,多条规则之间的验证关系。
知识补习:
哎,自己通过字节码生成Class,比如如果生成的Class里面有while循环的行为呢?安全性方面如何保证呢?
有了:
- 使用一个自定义的ClassLoader加载动态生成的Class。
- 对自定义的ClassLoader做安全配置,限制代码行为。
//P1 DynamicClassLoader
// ProtectionDomain表示代码源和权限的集合,里面封装了代码源(即代码从哪里来,来源是哪里)和权限(即代码有什么权限,能干嘛)的信息。
public class DynamicClassLoader extends ClassLoader {
private static final DynamicClassLoader instance = new DynamicClassLoader();
private static final java.security.ProtectionDomain DOMAIN;
static {
DOMAIN = (java.security.ProtectionDomain) java.security.AccessController.doPrivileged(
(PrivilegedAction<Object>) DynamicClassLoader.class::getProtectionDomain
);
}
}
//P2 ObjectReaderCreatorASM
//使用DynamicClassLoader,初始化ObjectReaderCreatorASM的classLoader属性。
public ObjectReaderCreatorASM(ClassLoader classLoader) {
this.classLoader = classLoader instanceof DynamicClassLoader ? (DynamicClassLoader) classLoader : new DynamicClassLoader(classLoader);
}
//P3 ObjectReaderCreatorASM.jitObjectReader
//调用classLoader的defineClassPublic方法,将code中的数据定义为一个新的类,并返回这个类的Class对象,赋值给readerClass。
byte[] code = cw.toByteArray();
Class<?> readerClass = classLoader.defineClassPublic(classNameFull, code, 0, code.length);
//P4 com.alibaba.fastjson2.util.DynamicClassLoader#defineClassPublic
// 定义类时候使用DOMAIN作为限制。
public Class<?> defineClassPublic(String name, byte[] b, int off, int len) throws ClassFormatError {
return defineClass(name, b, off, len, DOMAIN);
}
知识补习:
属性读写顺序
哎,读取和写入Key的顺序如何保证呢?先写哪些属性,后写哪些属性?如何将性能最大化?
有了:我可以在生成字节码阶段做一些事情
在生成字节码阶段,获取JavaBean所有属性列表,且将这些属性按照Hash值排序,ListA。
在生成字节码阶段,遍历ListA属性,生成字节码写入逻辑,这样我的代码里从上至下的编码顺序,即是正确的读取写入顺序。
在执行字节码类阶段,通过一个全局作用域的Byte数组,来维护JSON信息,不断在Byte数组后追加信息。
在执行字节码类阶段,使用Unsafe类操作内存设置值,避免频繁安全检查。
// P1 com.alibaba.fastjson2.writer.ObjectWriterCreatorASM#createObjectWriter
// 1.遍历JavaBean的属性,获取fieldWriters
BeanUtils.declaredFields(objectClass, field -> {
FieldWriter fieldWriter = creteFieldWriter(objectClass, writerFieldFeatures, provider, beanInfo, fieldInfo, field);
...
fieldWriterMap.put(fieldWriter.fieldName, fieldWriter);
});
fieldWriters = new ArrayList<>(fieldWriterMap.values());
// P2 com.alibaba.fastjson2.writer.ObjectWriterCreatorASM#genMethodWrite
// 2.遍历fieldWriters,按先后次序生成字节码
for (int i = 0; i < fieldWriters.size(); i++) {
FieldWriter fieldWriter = fieldWriters.get(i);
gwFieldValue(mwc, fieldWriter, OBJECT, i);
}
// P3 https://note.youdao.com/s/W9gi9fRD,(生成的WriterAdapter字节码)
// 3.如下为生成的字节码的编码顺序,
if ((var14 = ((ExcelTransformReq)var2).getColumn1()) != null)
...
if ((var14 = ((ExcelTransformReq)var2).getColumn10()) != null)
// P4 设置byte数值
// 4.写入数据
com.wang.track.analysis.OWG_1_25_ExcelTransformReq#write
// 4.2 具体filed写入,其中JSONWriter里面维护全局作用域,以及全局Byte[]
com.alibaba.fastjson2.writer.FieldWriter#writeFieldName
com.alibaba.fastjson2.JSONWriter#writeName
// 4.2.1 使用unsafe直接操作数组
UNSAFE.putLong(bytes, ARRAY_BYTE_BASE_OFFSET + off, name);
知识补习:
多种编码格式的适配
哎,字符串的编码格式种类,我如何适配到多种编码格式呢?
有了:其实不同编码格式的区别无非是,存储容器,拼接与位移策略,在不同的情况下的不同。
- 定义抽象一个类叫JSONWriter,这个类里面不做具体实现,只做方法的定义,与核心解析逻辑编写。(模板方法设计模式)
- 继承JSONWriter实现多种编码格式的实现类,比如:JSONWriterJSONB,JSONWriterUTF16,JSONWriterUTF8。
- 生成JSON时,根据情况选中具体JSONWriter实现。
- 将JSONWriter与WriterAdapter,使用桥接模式进行组装执行。(桥接设计模式)
// 如下列出了JSONWriter,已经它的具体实现
com.alibaba.fastjson2.JSONWriter
com.alibaba.fastjson2.JSONWriterUTF16
com.alibaba.fastjson2.JSONWriterUTF8
com.alibaba.fastjson2.JSONWriterJSONB
// 根据配置项,或者环境情况,初始化对应的实现。
// com.alibaba.fastjson2.JSONWriter#of
...
if (FIELD_STRING_VALUE != null && !ANDROID && !OPENJ9) {
jsonWriter = new JSONWriterUTF16JDK8UF(context);
} else {
jsonWriter = new JSONWriterUTF16JDK8(context);
}
...
// 将JSONWriter作为入参,传入WriterAdapter,使用桥接的思路。
com.wang.track.analysis.OWG_1_25_ExcelTransformReq#write(JSONWriter var1, Object var2, Object var3, Type var4, long var5)
this.fieldWriter0.writeFieldName(var1);
知识补习:
哎,复杂对象(一个class里有另一个class),该如何处理?
有了:
-
我可以使用懒加载的思路,在读取每个属性值的时候,先判断一下该属性类型,是否是复杂类型,如果是,则递归使用创建复杂类型的Reader,思路和如上类似。
// com.wang.track.analysis.ORG_1_25_ExcelTransformReq // ObjectReader 类里封装了递归生成reader的逻辑 public FieldReader fieldReader0; public ObjectReader objectReader0; // com.alibaba.fastjson2.reader.ObjectReader1#readJSONBObject long hashCode = jsonReader.readFieldNameHashCode(); if (hashCode == getTypeKeyHash() && i == 0) { long typeHash = jsonReader.readTypeHashCode(); JSONReader.Context context = jsonReader.getContext(); ObjectReader autoTypeObjectReader = autoType(context, typeHash); }
-
使用一个容器(map结构),将本次流程中所创建的reader,使用,classA-xxxReader,的形式缓存。
// com.alibaba.fastjson2.reader.ObjectReaderProvider // 如下是各种查询的缓存Map final ConcurrentMap<Type, ObjectReader> cache = new ConcurrentHashMap<>(); final ConcurrentMap<Type, ObjectReader> cacheFieldBased = new ConcurrentHashMap<>(); final ConcurrentMap<Integer, ConcurrentHashMap<Long, ObjectReader>> tclHashCaches = new ConcurrentHashMap<>(); final ConcurrentMap<Long, ObjectReader> hashCache = new ConcurrentHashMap<>(); final ConcurrentMap<Class, Class> mixInCache = new ConcurrentHashMap<>();
代码结构图
如下是最终的架构图:
[图片上传失败...(image-dc35c6-1707987987703)]
1.入口
JSON.toJSONString(testBean);
2.初始化ObjectWriterProvider(默认ASM实现),全局上下文(context)
final ObjectWriterProvider provider = defaultObjectWriterProvider; final JSONWriter.Context context = new JSONWriter.Context(provider);
3.初始化JSONWriter(判断当前所处环境,以及参数,自动选择合适的JSONWriter)
JSONWriter writer = JSONWriter.of(context)
3.初始化getObjectWriter
// 初始化getObjectWriter ObjectWriter<?> objectWriter = provider.getObjectWriter(valueClass,valueClass,(defaultWriterFeatures & JSONWriter.Feature.FieldBased.mask) != 0);
4.解析
// 此时objectWriter默认的类型为....OWG_1_25_ExcelTransformReq,为ASM生成的class objectWriter.write(writer, object, null, null, 0);
5.具体的字符串输出
return writer.toString();
反序列化设计(Json->Bean)
由于parseObject方法的代表性,如下均以com.alibaba.fastjson2.JSON#parseObject方法作为思路解析的依据
其实作者对序列化,反序列化的思路是差不多的。只不过反序列化有一些区别,这部分只针对有区分的部分做推倒。
- Json转为Bean时,key的顺序不可预测。
- JSON->Bean,只需要调用Bean的set方法,所以不需要考虑反射性能差的原因。
key无序问题
哎,JSON的key的顺序是不可预测的,我该如何处理?
有了:相同字符串的Hash值是一样的,我可以基于这个规律,在写字节码的时候,按顺序写不就行了,通过Hash来作为IF的判断依据。
可能有同学有疑问,使用Map结构足够了,为什么还有生成这样的代码
因为在key的数量相当有限的情况下,逻辑分支的性能(经过JVM指令优化)远比Map寻址的性能高。
// 根据Name的Hash值,使用switch编写逻辑分支代码。
var9 = var1.readFieldNameHashCode()
int var11 = (int)(var9 ^ var9 >>> 32);
switch(var11) {
case 1079836942:
if (var9 == 3832966174269534051L) {
var10001 = var1.readString();
((ExcelTransformReq)var6).setColumn15(var10001);
continue;
}
break;
case 1079902478:
if (var9 == 3833247649246244707L) {
var10001 = var1.readString();
((ExcelTransformReq)var6).setColumn25(var10001);
continue;
}
break;
...
代码结构图
[图片上传失败...(image-c9fc78-1707987987703)]
1.入口
com.alibaba.fastjson2.JSON#parseObject
2.初始化ObjectReaderProvider,与Context(全局上下文)
ObjectReaderProvider provider = JSONFactory.getDefaultObjectReaderProvider(); JSONReader.Context context = new JSONReader.Context(provider);
3.初始化objectReader
ObjectReader<T> objectReader = provider.getObjectReader(clazz,(defaultReaderFeatures & JSONReader.Feature.FieldBased.mask) != 0);
4.初始化JSONReader
JSONReader reader = JSONReader.of(text, context);
5.解析
T object = objectReader.readObject(reader, clazz, null, 0);
环境变量设计
环境变量信息搜集
哎,我需要面对不同的环境,如果有一个全局视野知道当前环境的情况那些功能可用?
有了,我可以使用一个JDKUtils,所有当前环境的信息,都通过各种方式取出来,然后放到它里面统一维护,用这个类来解决环境变量问题。
-
JVM版本等,通过系统参数获取。(类加载阶段,static代码块)
System.getProperty("java.vm.name")
-
判断当前环境(安卓还是JavaWeb),通过寻找Class的方式获取(类加载阶段,static代码块)
Class<?> factorClass = Class.forName("java.lang.management.ManagementFactory");
-
框架公共方法,通过MethodHandles获取(运行阶段,方法首次调用时)
MethodHandles.Lookup lookup = trustedLookup(classStringCoding);
CallSite callSite = LambdaMetafactory.metafactory(..);
isAscii = (Predicate<byte[]>) callSite.getTarget().invokeExact();PREDICATE_IS_ASCII = isAscii;
// P1
// 环境工具类
com.alibaba.fastjson2.util.JDKUtils
// static阶段获取
public static final Unsafe UNSAFE;
public static final long ARRAY_BYTE_BASE_OFFSET;
public static final long ARRAY_CHAR_BASE_OFFSET;
public static final int JVM_VERSION;
public static final Field FIELD_STRING_VALUE;
// 如下是通过BiFunction(函数式编程),将不同环境中的能力统一出口
public static final ToIntFunction<String> STRING_CODER;
public static final Function<String, byte[]> STRING_VALUE;
// P2,如下是获取安卓SDK版本号
android_sdk_int = Class.forName("android.os.Build$VERSION").getField("SDK_INT").getInt(null);
// p3,获取Java版本号
String javaSpecVer = System.getProperty("java.specification.version");
// P4,判断当前环境是否有sql的jar包
dataSourceClass = Class.forName("javax.sql.DataSource");
// P5,根据不同版本通过Lambda表达式封装实现
if (JVM_VERSION >= 17) {
handle = trustedLookup.findStatic(classStringCoding =
String.class,"isASCII",MethodType.methodType(boolean.class,byte[].class);
}
....
if (handle == null && JVM_VERSION >= 11) {
classStringCoding = Class.forName("java.lang.StringCoding");
handle = trustedLookup.findStatic(classStringCoding,"isASCII",MethodType.methodType(boolean.class, byte[].class)
}
MethodHandles.Lookup lookup = trustedLookup(classStringCoding);
CallSite callSite = LambdaMetafactory.metafactory(..);
isAscii = (Predicate<byte[]>) callSite.getTarget().invokeExact();
PREDICATE_IS_ASCII = isAscii;
流程数据维护和流程能力扩展
哎,全局视野如何保证?比如现在多层嵌套的JSON,由于我使用Byte[]维护。
有了:
在解析开始时候,设置一个全局Context,这个Context贯穿流程始终。
- 维护当前进行中的值,或者解析状态值,比如JSON数据,当前写入偏移量等。
- 维护一些全局配置,比如用户配置的功能属性开关。
- 使用Long值+位移,来实现不同能力的开关。
哎,功能参数和扩展性如何设计呢?
- 配置项比较多,目前有四五十个,后面可能还会拓展到百个。
- 需要再解析前后加一些钩子。
有了:
-
配置项可以统一使用Long+位移的方式,这样一个Long值,就能代表63个配置项,极大减少空间。
// com.alibaba.fastjson2.JSONReader.Feature public enum Feature { FieldBased(1), IgnoreNoneSerializable(1 << 1), ErrorOnNoneSerializable(1 << 2), SupportArrayToBean(1 << 3), InitStringFieldAsEmpty(1 << 4) }
-
其实扩展性无非是俩种类型,一种是影响主逻辑的,一种是不影响主逻辑(单纯是生命周期回调),第1种我可以使用Lambda表达式,第2种我可以使用Processor思路去做(统一维护触发对象,在特定实际触发)。
1.生命周期触发
2.置入代码调整主逻辑。
使用全局Context,维护所有的扩展配置,使这些配置在全流程中可见,被动触发不受限制。
// 分别是序列化,反序列化,的Context
com.alibaba.fastjson2.JSONWriter.Context
com.alibaba.fastjson2.JSONReader.Context
// 如下是JSONReader.Context的部分定义,其中objectSupplier,arraySupplier为
public static final class com.alibaba.fastjson2.JSONReader.Context {
long features;
// 扩展点:第1种
Supplier<Map> objectSupplier;
Supplier<List> arraySupplier;
// 扩展点:第2种
AutoTypeBeforeHandler autoTypeBeforeHandler;
ExtraProcessor extraProcessor;
}
// 如下是JSONWriter.Context的部分定义,
public static final class com.alibaba.fastjson2.JSONWriter.Context {
long features;
boolean hasFilter;
// 扩展点:第2种
PropertyPreFilter propertyPreFilter;
PropertyFilter propertyFilter;
NameFilter nameFilter;
ValueFilter valueFilter;
BeforeFilter beforeFilter;
AfterFilter afterFilter;
...
}
哎,这些扩展点,需要通过不同的入参传入,体验不太友好,因为这些配置项到底是哪个层面的,没有办法客观区分出来?
答:有了,使用参数注解,属性注解天生就具备一个能力:该注解生效在哪个属性上!
高低版本JDK如何兼容
哎,不同环境可能有一些能力的实现,或者方法名称不一样?我是否可以借力其他框架的某一些能力?
有了:上面好像用过 Lambda表达式的一种能力,将不同环境的实现,统一入口。
CallSite callSite = LambdaMetafactory.metafactory(..);
我可以基于这个思路,通过Class.forName,寻找出当前环境能提供的内容,甚至可以查找一些三方包。
只要符合要求的,我都包装进来
- 这样即便某些低版本的JDK也可以使用高版本的一些能力(如果它引用的一些好的三方包)。
- 模板方法的核心逻辑封装不需要考虑环境问题(调用入口已经统一)。
方法的传递,其实使用MethodHandle已经足够了,是否再将MethodHandle包装为Lambda,取决于是否要统一调用入口。
所以如下,既有MethodHandle,也有Lambda(BiFunction,ToIntFunction)
public static final BiFunction<char[], Boolean, String> STRING_CREATOR_JDK8; // JDK 8 的 String 创建函数
public static final BiFunction<byte[], Byte, String> STRING_CREATOR_JDK11; // JDK 11 的 String 创建函数
public static final ToIntFunction<String> STRING_CODER; // String 的编码函数
public static final Function<String, byte[]> STRING_VALUE; // String 的值函数
public static final MethodHandle METHOD_HANDLE_HAS_NEGATIVE; // 是否有负数的方法句柄
public static final Predicate<byte[]> PREDICATE_IS_ASCII; // 是否为 ASCII 的谓词
如下则是通过其他框架来扩展自身能力:
// com.alibaba.fastjson2.writer.ObjectWriterProvider#getObjectWriterInternal
switch (className) {
case "com.google.common.collect.HashMultimap":
case "com.google.common.collect.LinkedListMultimap":
case "com.google.common.collect.LinkedHashMultimap":
case "com.google.common.collect.ArrayListMultimap":
case "com.google.common.collect.TreeMultimap":
objectWriter = GuavaSupport.createAsMapWriter(objectClass);
break;
case "com.google.common.collect.AbstractMapBasedMultimap$RandomAccessWrappedList":
objectWriter = ObjectWriterImplList.INSTANCE;
break;
case "com.alibaba.fastjson.JSONObject":
objectWriter = ObjectWriterImplMap.of(objectClass);
break;
case "android.net.Uri$OpaqueUri":
case "android.net.Uri$HierarchicalUri":
case "android.net.Uri$StringUri":
objectWriter = ObjectWriterImplToString.INSTANCE;
break;
default:
break;
}
性能提升的思路
- Lambada表达式+MethodHandler,替换反射调用。
- Unsafe方法,替换反射调用set方法。
- 多维度缓存,确保在高频调用逻辑均由缓存承载。
- 使用全局上下文,维护复用全局变量,减少重复对象的生成。
- 编码相关:位移操作代替加减乘除;数组索引代替map中的key;减少容器自动扩容行为;尽量在类加载阶段获取信息;尽可能的使用IdentityHashMap。
- CodeGen算法使用。
- 使用字节码提升JSONPath性能。
- 使用Fnv算法代替原生的hash算法。