Preface

Above the ASM introductory chapter in addition to ASM used made the introduction, also referred to ASM some scenarios being used, which is a ASM be used instead Java reflector; FastJson as sequence tools, on the use of ASM To replace the use of reflection, improve the overall performance.

Serialization

Serialization is to convert the object into Json format, and then use it for persistence or network transmission; FastJson provides the interface class ObjectSerializer :

public interface ObjectSerializer {
    
    void write(JSONSerializer serializer, //
               Object object, //
               Object fieldName, //
               Type fieldType, //
               int features) throws IOException;
}
  • serializer: serialization context;
  • object: The object that needs to be converted to Json;
  • fieldName: the field name of the parent object;
  • fieldType: parent object field type;
  • features: serialized features of the parent object field;

There are many implementation classes for ObjectSerializer JavaBean JavaBeanSerializer . Of course, there are some other types of serialization tool classes such as basic types, date types, enumeration types, some special types, etc.; the introduced ASM is positive It is JavaBeanSerializer . Of course, it is conditional to use ASM optimization. If the conditions are met, ASM will generate a JavaBeanSerializer instead of the reflection operation inside;

Activation conditions

FastJson provides the ASM switch, which is turned on by default. At the same time, ASMSerializerFactory can be used to create ASMSerializer under certain conditions;

switch

SerializeConfig asm switch identification is provided in 060cc053a43f04:

private boolean asm = !ASMUtils.IS_ANDROID

In the default Android environment, it is false , otherwise it is true . Asm is generally enabled for all server development;

JSONType annotation

  • If serializer is configured, the configured serialization class is used:
@JSONType(serializer = JavaBeanSerializer.class)
  • If asm is false asm is not enabled:
@JSONType(asm = false)
  • If several types of SerializerFeature asm is not enabled:
@JSONType(serialzeFeatures = {SerializerFeature.WriteNonStringValueAsString,SerializerFeature.WriteEnumUsingToString,SerializerFeature.NotWriteDefaultValue,SerializerFeature.BrowserCompatible})
  • If you configure SerializeFilter , not open asm :
@JSONType(serialzeFilters = {})

BeanType class information

If the class modifier is not public , 060cc053a44044 is not turned on, and asm is used JavaBeanSerializer ;

If the characters of the class name include <001 || >177 || ==. asm is not enabled;

If the class is an interface class, asm is not enabled;

Check the class attributes, including type, return value, annotations, etc. asm is also not enabled in the case of non-compliance;

Create ASMserializer

Generate an independent JavaBeanSerializer subclass for each JavaBean through ASM , the specific steps are as follows:

Generate class name

Before creating a class, you need to generate a unique name:

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

Here seed is a AtomicLong variable; taking Person as an example, the generated className is ASMSerializer_1_Person ;

Generate subcategories

By the ASM ClassWriter generates JavaBeanSerializer subclass, rewriting write method, JavaBeanSerializer in write method uses reflected from JavaBean obtaining related information, and generated by ASM ASMSerializer_1_Person , against Person unique sequence of tools, can be See part of the code:

public class ASMSerializer_1_Person
extends JavaBeanSerializer
implements 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);
    }
    ...省略...
}

Because it is only Person serialization tool, all can be found directly inside the strong turn Object as Person , obtained by directly calling the Person information, replacing the use of reflection, we know directly call the performance a lot stronger than reflection;

View source code

JavaBeanSerializer subclass generated by ASM is converted into a byte array and loaded directly into the memory through class loading. If you want to view the automatically generated class source code, you can use the following two methods to obtain:

  • Add Debug code
    Find the createJavaBeanSerializer method in ASMSerializerFactory , the code generated by ASM will eventually generate a byte array, part of the code is as follows:

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

Breakpoints may be added at the second line in the IDEA environment, then right-click on the breakpoint, select More , check Evaluate and log , enter the following code:

FileOutputStream fileOutputStream = new FileOutputStream(new File("F:/ASMSerializer_1_Person.class"));
fileOutputStream.write(code);
fileOutputStream.close();
  • Use arthas
    Because we already know the automatically generated class name, we can use arthas monitor the current process, and then use the jad command to get the class source code:

    [arthas@17916]$ jad com.alibaba.fastjson.serializer.ASMSerializer_1_Person
    
    ClassLoader:
    +-com.alibaba.fastjson.util.ASMClassLoader@32eebfca
      +-jdk.internal.loader.ClassLoaders$AppClassLoader@2437c6dc
        +-jdk.internal.loader.ClassLoaders$PlatformClassLoader@3688c050
    
    Location:
    /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_Person
    extends JavaBeanSerializer
    implements ObjectSerializer {
        public ASMSerializer_1_Person(SerializeBeanInfo serializeBeanInfo) {

Note that you need to provide the full name of the class: package name + class name

Load class

The class information generated by ASM cannot be used directly. It also needs to be loaded by class loading. Here, it ASMClassLoader . After loading, obtain the constructor Constructor , and then use newInstance create a 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);

Deserialization

Json string into an object, FastJson provides ObjectDeserializer interface class:

public interface ObjectDeserializer {
    <T> T deserialze(DefaultJSONParser parser, Type type, Object fieldName);
    int getFastMatchToken();
}
  • parser: Deserialization context DefaultJSONParser;
  • type: the type of object to be deserialized;
  • fieldName: the field name of the parent object;

The same serialization process is roughly divided into the following processes:

Generate class name

Generate a deserialization tool class name for a business class:

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

Also take Person as an example, then the generated className is FastjsonASMDeserializer_1_Person ;

Generate subcategories

Also used ASM of ClassWriter generating JavaBeanDeserializer subclass, wherein the rewriting deserialze method, part of the code as follows:

public class FastjsonASMDeserializer_1_Person
extends 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);
    }
    ...省略...
}

It can be found that the same specific business class is used instead of the JavaBeanDeserializer ; the same type of information needs to be loaded at the end, and then the specific business object is instantiated through the constructor.

to sum up

The use of ASM instead of reflection in this article is just one of the many usage scenarios. In FastJson , only the generation class function ASM ASM is its conversion function, which transforms existing classes and generates new classes. Existing classes are enhanced. Of course, the loading process is not simply a class loader. At this time, use Java Agent to achieve hot update; in fact, instead of reflection, instead of using ASM , a method is also provided Protobuf At this stage, all business serialization and deserialization operations are prepared, which is a static processing method, and ASM is more user-friendly.


ksfzhaohui
398 声望70 粉丝

« 上一篇
ASM入门篇