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
isfalse
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 openasm
:
@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 thecreateJavaBeanSerializer
method inASMSerializerFactory
, 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 usearthas
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.
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。