前言

上文ASM入门篇中除了对ASM的应用做了介绍,同时也提到ASM被应用的一些场景,其中有一项就是ASM被用来代替Java反射;FastJson作为序列化工具,就应用了ASM来代替反射的应用,进步整体的性能。

序列化

序列化就是将对象转换成Json格局的字符串,而后用来长久化或者网络传输;FastJson提供了接口类ObjectSerializer

public interface ObjectSerializer {        void write(JSONSerializer serializer, //               Object object, //               Object fieldName, //               Type fieldType, //               int features) throws IOException;}
  • serializer:序列化上下文;
  • object:须要转换为Json的对象;
  • fieldName:父对象字段名称;
  • fieldType:父对象字段类型;
  • features:父对象字段序列化features;

ObjectSerializer的实现类有多种,最常见就是序列化JavaBeanJavaBeanSerializer,当然还有一些其余类型的序列化工具类比方:根本类型、日期类型、枚举类型、一些非凡类型等等;引入的ASM正是对JavaBeanSerializer的优化,当然要应用ASM优化也是有条件的,满足条件的状况下会通过ASM生成JavaBeanSerializer的子类,代替外面的反射操作;

启用条件

FastJson提供了ASM的开关,默认开启状态,同时在满足肯定的条件下能力应用ASMSerializerFactory创立ASMSerializer

开关

SerializeConfig中提供了asm开关标识:

private boolean asm = !ASMUtils.IS_ANDROID

默认安卓环境下为false,否则为true,所有服务端开发个别都是开启asm的;

JSONType注解

  • 如果配置了serializer,则应用配置的序列化类:
@JSONType(serializer = JavaBeanSerializer.class)
  • 如果配置了asmfalse,则不启用asm
@JSONType(asm = false)
  • 如果配置了指定几种SerializerFeature,则不开启asm
@JSONType(serialzeFeatures = {SerializerFeature.WriteNonStringValueAsString,SerializerFeature.WriteEnumUsingToString,SerializerFeature.NotWriteDefaultValue,SerializerFeature.BrowserCompatible})
  • 如果配置了SerializeFilter,则不开启asm
@JSONType(serialzeFilters = {})

BeanType类信息

如果类修饰符不为public,则不开启asm,间接应用JavaBeanSerializer

如果类名称字符中蕴含了<001 || >177 || ==.的字符,则不启用asm

如果类是接口类则不启用asm

对类属性的查看,包含类型,返回值,注解等,不合乎的状况下同样不启用asm

创立ASMSerializer

通过ASM为每个JavaBean生成一个独立的JavaBeanSerializer子类,具体步骤如下:

生成类名

创立类之前须要生成一个惟一的名称:

String className = "ASMSerializer_" + seed.incrementAndGet() + "_" + clazz.getSimpleName()

这里的seed是一个AtomicLong变量;以Person为例,则生成的classNameASMSerializer_1_Person

生成子类

通过ASM的ClassWriter来生成JavaBeanSerializer的子类,重写write办法,JavaBeanSerializer中的write办法会应用反射从JavaBean中获取相干信息,而通过ASM生成的ASMSerializer_1_Person,是针对Person独有的序列化工具类,能够看局部代码:

public class ASMSerializer_1_Personextends JavaBeanSerializerimplements ObjectSerializer {    public ASMSerializer_1_Person(SerializeBeanInfo serializeBeanInfo) {        super(serializeBeanInfo);    }    public void write(JSONSerializer jSONSerializer, Object object, Object object2, Type type, int n) throws IOException {        if (object == null) {            jSONSerializer.writeNull();            return;        }        SerializeWriter serializeWriter = jSONSerializer.out;        if (!this.writeDirect(jSONSerializer)) {            this.writeNormal(jSONSerializer, object, object2, type, n);            return;        }        if (serializeWriter.isEnabled(32768)) {            this.writeDirectNonContext(jSONSerializer, object, object2, type, n);            return;        }        Person person = (Person)object;        if (this.writeReference(jSONSerializer, object, n)) {            return;        }        if (serializeWriter.isEnabled(0x200000)) {            this.writeAsArray(jSONSerializer, object, object2, type, n);            return;        }        SerialContext serialContext = jSONSerializer.getContext();        jSONSerializer.setContext(serialContext, object, object2, 0);        int n2 = 123;        String string = "email";        String string2 = person.getEmail();        if (string2 == null) {            if (serializeWriter.isEnabled(132)) {                serializeWriter.write(n2);                serializeWriter.writeFieldNameDirect(string);                serializeWriter.writeNull(0, 128);                n2 = 44;            }        } else {            serializeWriter.writeFieldValueStringWithDoubleQuoteCheck((char)n2, string, string2);            n2 = 44;        }        string = "id";        int n3 = person.getId();        serializeWriter.writeFieldValue((char)n2, string, n3);        n2 = 44;        string = "name";        string2 = person.getName();        if (string2 == null) {            if (serializeWriter.isEnabled(132)) {                serializeWriter.write(n2);                serializeWriter.writeFieldNameDirect(string);                serializeWriter.writeNull(0, 128);                n2 = 44;            }        } else {            serializeWriter.writeFieldValueStringWithDoubleQuoteCheck((char)n2, string, string2);            n2 = 44;        }        if (n2 == 123) {            serializeWriter.write(123);        }        serializeWriter.write(125);        jSONSerializer.setContext(serialContext);    }    ...省略...}

因为是仅是Person的序列化工具,所有能够发现外面间接强转ObjectPerson,通过间接调用的形式获取Person的相干信息,替换了反射的应用,咱们晓得间接调用的性能比应用反射强很多;

查看源码

通过ASM生成的JavaBeanSerializer子类,转换成字节数组通过类加载间接加载到内存中,如果想查看主动生成的类源码能够应用如下两种形式来获取:

  • 增加Debug代码
    ASMSerializerFactory中找到createJavaBeanSerializer办法,ASM生成的代码最终会生成字节数组,局部代码如下所示:

    byte[] code = cw.toByteArray();Class<?> serializerClass = classLoader.defineClassPublic(classNameFull, code, 0, code.length);

在IDEA环境下能够在第二行处加断点,而后右击断点,抉择More,勾选Evaluate and log,输出如下代码:

FileOutputStream fileOutputStream = new FileOutputStream(new File("F:/ASMSerializer_1_Person.class"));fileOutputStream.write(code);fileOutputStream.close();
  • 应用arthas
    因为咱们曾经晓得主动生成的类名,能够应用arthas监控以后过程,而后应用jad命令获取类源码:

    [arthas@17916]$ jad com.alibaba.fastjson.serializer.ASMSerializer_1_PersonClassLoader:+-com.alibaba.fastjson.util.ASMClassLoader@32eebfca  +-jdk.internal.loader.ClassLoaders$AppClassLoader@2437c6dc    +-jdk.internal.loader.ClassLoaders$PlatformClassLoader@3688c050Location:/D:/myRepository/com/alibaba/fastjson/1.2.70/fastjson-1.2.70.jar/* * Decompiled with CFR. * * Could not load the following classes: *  com.fastjson.Person */package com.alibaba.fastjson.serializer;import com.alibaba.fastjson.serializer.JSONSerializer;import com.alibaba.fastjson.serializer.JavaBeanSerializer;import com.alibaba.fastjson.serializer.ObjectSerializer;import com.alibaba.fastjson.serializer.SerialContext;import com.alibaba.fastjson.serializer.SerializeBeanInfo;import com.alibaba.fastjson.serializer.SerializeWriter;import com.fastjson.Person;import java.io.IOException;import java.lang.reflect.Type;public class ASMSerializer_1_Personextends JavaBeanSerializerimplements ObjectSerializer {    public ASMSerializer_1_Person(SerializeBeanInfo serializeBeanInfo) {

留神这里须要提供类的全名称:包名+类名

加载类

通过ASM生成的类信息,不能间接应用,还须要通过类加载加载,这里通过ASMClassLoader来加载,加载完之后获取结构器Constructor,而后应用newInstance创立一个JavaBeanSerializer的子类:

byte[] code = cw.toByteArray();Class<?> serializerClass = classLoader.defineClassPublic(classNameFull, code, 0, code.length);Constructor<?> constructor = serializerClass.getConstructor(SerializeBeanInfo.class);Object instance = constructor.newInstance(beanInfo);

反序列化

Json串转化成对象的过程,FastJson提供了ObjectDeserializer接口类:

public interface ObjectDeserializer {    <T> T deserialze(DefaultJSONParser parser, Type type, Object fieldName);    int getFastMatchToken();}
  • parser:反序列化上下文DefaultJSONParser;
  • type:要反序列化的对象的类型;
  • fieldName:父对象字段名称;

同序列化过程,大抵分为以下几个过程:

生成类名

生成一个业务类的反序列化工具类名:

String className = "FastjsonASMDeserializer_" + seed.incrementAndGet() + "_" + clazz.getSimpleName();

同样以Person为例,那么生成的classNameFastjsonASMDeserializer_1_Person

生成子类

同样应用ASMClassWriter生成JavaBeanDeserializer的子类,重写其中的deserialze办法,局部代码如下:

public class FastjsonASMDeserializer_1_Personextends JavaBeanDeserializer {    public char[] name_asm_prefix__ = "\"name\":".toCharArray();    public char[] id_asm_prefix__ = "\"id\":".toCharArray();    public char[] email_asm_prefix__ = "\"email\":".toCharArray();    public ObjectDeserializer name_asm_deser__;    public ObjectDeserializer email_asm_deser__;    public FastjsonASMDeserializer_1_Person(ParserConfig parserConfig, JavaBeanInfo javaBeanInfo) {        super(parserConfig, javaBeanInfo);    }    public Object createInstance(DefaultJSONParser defaultJSONParser, Type type) {        return new Person();    }    public Object deserialze(DefaultJSONParser defaultJSONParser, Type type, Object object, int n) {        block18: {            String string;            int n2;            String string2;            int n3;            Person person;            block20: {                ParseContext parseContext;                ParseContext parseContext2;                block19: {                    block22: {                        JSONLexerBase jSONLexerBase;                        block24: {                            int n4;                            int n5;                            block23: {                                block21: {                                    String string3;                                    String string4;                                    jSONLexerBase = (JSONLexerBase)defaultJSONParser.lexer;                                    if (jSONLexerBase.token() == 14 && jSONLexerBase.isEnabled(n, 8192)) {                                        return this.deserialzeArrayMapping(defaultJSONParser, type, object, null);                                    }                                    if (!jSONLexerBase.isEnabled(512) || jSONLexerBase.scanType("com.fastjson.Person") == -1) break block18;                                    ParseContext parseContext3 = defaultJSONParser.getContext();                                    n5 = 0;                                    person = new Person();                                    parseContext2 = defaultJSONParser.getContext();                                    parseContext = defaultJSONParser.setContext(parseContext2, person, object);                                    if (jSONLexerBase.matchStat == 4) break block19;                                    n4 = 0;                                    n3 = 0;                                    boolean bl = jSONLexerBase.isEnabled(4096);                                    if (bl) {                                        n3 |= 1;                                        string4 = jSONLexerBase.stringDefaultValue();                                    } else {                                        string4 = null;                                    }                                    string2 = string4;                                    n2 = 0;                                    if (bl) {                                        n3 |= 4;                                        string3 = jSONLexerBase.stringDefaultValue();                                    } else {                                        string3 = null;                                    }                                    string = string3;                                    string2 = jSONLexerBase.scanFieldString(this.email_asm_prefix__);                                    if (jSONLexerBase.matchStat > 0) {                                        n3 |= 1;                                    }                                    if ((n4 = jSONLexerBase.matchStat) == -1) break block20;                                    if (jSONLexerBase.matchStat <= 0) break block21;                                    ++n5;                                    if (jSONLexerBase.matchStat == 4) break block22;                                }                                n2 = jSONLexerBase.scanFieldInt(this.id_asm_prefix__);                                if (jSONLexerBase.matchStat > 0) {                                    n3 |= 2;                                }                                if ((n4 = jSONLexerBase.matchStat) == -1) break block20;                                if (jSONLexerBase.matchStat <= 0) break block23;                                ++n5;                                if (jSONLexerBase.matchStat == 4) break block22;                            }                            string = jSONLexerBase.scanFieldString(this.name_asm_prefix__);                            if (jSONLexerBase.matchStat > 0) {                                n3 |= 4;                            }                            if ((n4 = jSONLexerBase.matchStat) == -1) break block20;                            if (jSONLexerBase.matchStat <= 0) break block24;                            ++n5;                            if (jSONLexerBase.matchStat == 4) break block22;                        }                        if (jSONLexerBase.matchStat != 4) break block20;                    }                    if ((n3 & 1) != 0) {                        person.setEmail(string2);                    }                    if ((n3 & 2) != 0) {                        person.setId(n2);                    }                    if ((n3 & 4) != 0) {                        person.setName(string);                    }                }                defaultJSONParser.setContext(parseContext2);                if (parseContext != null) {                    parseContext.object = person;                }                return person;            }            if ((n3 & 1) != 0) {                person.setEmail(string2);            }            if ((n3 & 2) != 0) {                person.setId(n2);            }            if ((n3 & 4) != 0) {                person.setName(string);            }            return (Person)this.parseRest(defaultJSONParser, type, object, person, n, new int[]{n3});        }        return super.deserialze(defaultJSONParser, type, object, n);    }    ...省略...}

能够发现同样应用具体的业务类,代替了在JavaBeanDeserializer应用反射操作;同样最初须要对该类信息进行加载,而后通过结构器实例化具体业务对象。

总结

本文应用ASM代替反射只是泛滥应用场景的一种,在FastJson中只用了ASM的生成类性能;ASM更强的的性能是它的转换性能,对现有类进行革新,生成新的类从而对现有类进行性能加强,当然加载的过程也不是简略的应用类加载器就行了,这时候配合Java Agent来实现热更新;其实代替反射除了应用ASM,在Protobuf中也提供了一种办法,在研发阶段就把所有的业务序列化反序列化操作都筹备好,是一种动态的解决形式,而ASM这种动静生成的形式更加人性化。