fastjson反序列化漏洞1-流程分析
环境搭建

先生成这种结构的项目:

然后在pom.xml中添加依赖:
<dependencies>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.24</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.5.1</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>1.7.25</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-nop</artifactId>
<version>1.7.25</version>
</dependency>
</dependencies>
然后就增加了依赖:

基础知识
FastJson是Java语言编写的高性能功能完善的JSON库,对象转换为JSON格式
漏洞主要围绕着setter、getter方法
漏洞版本
1.2.24版本以下,漏洞产生的原因主要是没有对序列化
1.2.25-1.2.41 版本增加了黑名单限制
1.2.68 使用类AutoCloseable来绕过fastjson校验
参考
https://www.bilibili.com/video/BV1bD4y117Qh?t=7.0
示例代码

package org.example.demo;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
public class JSONUser {
public static void main(String[] args) throws Exception {
String s = "{\"param1\":\"aaa\",\"param2\":\"bbb\"}";
JSONObject jsonObject = JSON.parseObject(s);
System.out.println(jsonObject);
System.out.println(jsonObject.getString("param1"));
}
}
fastjson的作用就是将字符串转换为JSON,然后可以进行JSON操作
如:
String s = "{\"age\":18,\"name\":\"abc\"}";
Person person = JSON.parseObject(s,Person.class); // 直接解析为一个Person对象
System.out.println(person.getName());
实际上是调用了construtor -> setAge ->setName -> getName -> abc
另一个示例:
// Person.java
package org.example.demo;
import java.util.Map;
public class Person {
private Integer age;
private String name;
private Map map;
public Person() {
System.out.println("constructor");
}
public Person(Integer age, String name) {
this.age = age;
this.name = name;
}
public Integer getAge() {
System.out.println("getAge");
return age;
}
public void setAge(Integer age) {
System.out.println("setAge");
this.age = age;
}
public String getName() {
System.out.println("getName");
return name;
}
public void setName(String name) {
System.out.println("setName");
this.name = name;
}
public Map getMap(){ // 有set就没get,所以这里不能有setMap
return map;
}
}
// fastjson.java
package org.example.demo;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
public class JSONUser {
public static void main(String[] args) throws Exception {
// String s = "{\"param1\":\"aaa\",\"param2\":\"bbb\"}";
// String s = "{\"age\":18,\"name\":\"abc\"}";
// String s = "{\"@type\":\"org.example.demo.Person\",\"age\":18,\"name\":\"ttt\"}";
String s = "{\"@type\":\"org.example.demo.Test\",\"cmd\":\"calc\"}";
JSONObject jsonObject = JSON.parseObject(s);
System.out.println(jsonObject);
}
}
@type指定为Person类,然后
按照Person类进行解析、实例化、赋值、调用,全做了一遍
这个constructor是空参构造函数里面的
而这个行为不是代码端控制的,而是客户端控制的,会根据传入的字符串的不同,然后找不同的代码路径
可能类似于一句话木马,传入不同的字符串,执行不同的代码
流程分析
第一次调试

跟进:

发现进入接收String类型的parseObject
public static JSONObject parseObject(String text) {
Object obj = parse(text);
if (obj instanceof JSONObject) {
return (JSONObject) obj;
}
return (JSONObject) JSON.toJSON(obj);
}
这个parseObject方法的作用是解析传入的字符串text,将其转换为一个JSONObject对象
关于这个JSONObject:

实现了map
继续跟进parse方法:

跟进:

看到这里使用了DefaultJSONParser方法
字符串传进去后会调用DefaultJSONParser进行解析
DefaultJSONParser parser = new DefaultJSONParser(text, ParserConfig.getGlobalInstance(), features);
ParserConfig.getGlobalInstance() 指ParserConfig的实例化对象
后面有个features,代表着解析时的规则要求,如能不能用单引号,传入了空格怎么处理
继续跟进:

步入,来到了DefaultJSONParser类的parse方法:

继续跟进:

final JSONLexer lexer = this.lexer;
switch (lexer.token()) {
case SET:
lexer.nextToken();
HashSet<Object> set = new HashSet<Object>();
parseArray(set, fieldName);
return set;
case TREE_SET:
lexer.nextToken();
TreeSet<Object> treeSet = new TreeSet<Object>();
parseArray(treeSet, fieldName);
return treeSet;
case LBRACKET:
JSONArray array = new JSONArray();
parseArray(array, fieldName);
if (lexer.isEnabled(Feature.UseObjectArray)) {
return array.toArray();
}
return array;
首先声明了一个名为lexer的JSONLexer对象,他是当前对象的成员变量this.lexer的引用
lexer.token()获取当前令牌,并根据这个令牌的值执行不同的分支
现在就是字符串挨个去匹配

case LBRACKET 为左中括号
case LBRACE 为左大括号

现在的第一个字符就是左大括号,所以进入了CASE LBRACE
case LBRACE:
JSONObject object = new JSONObject(lexer.isEnabled(Feature.OrderedField));
return parseObject(object, fieldName);
这个分支处理JSON对象,它创建一个JSONObject对象,并调用parseObject方法来填充对象,并返回填充好的对象
跟进这个parseObject方法:

这个parseObject方法还挺长的。。。。
它根据不同的JSON数据类型来解析JSON字符串,并返回相应的Java map对象,这个方法依赖于JSONLexer类来逐个读取JSON字符串中的令牌,并依赖于parseArray和parseObject方法来递归解析数组和对象
对边界值的判断:


这里新建了一个key,之后就要解析这个key了

key = lexer.scanSymbol(symbolTable, '"');
这一行把双引号之间的值读出来,即@type
读到key之后:

进行匹配
JSON.DEFAULT_TYPE_KEY
指的是@type
匹配到这个@type的话说明做的事是Java的反序列化,而不只是json的
if (key == JSON.DEFAULT_TYPE_KEY && !lexer.isEnabled(Feature.DisableSpecialKeyDetect)) {
String typeName = lexer.scanSymbol(symbolTable, '"');
Class<?> clazz = TypeUtils.loadClass(typeName, config.getDefaultClassLoader());
使用了loadClass加载这个类
跟进这个loadClass查看:
来到了TypeUtils类的loadClass

Class<?> clazz = mappings.get(className);
指先从内存中查找,如果有的话直接获取,相当于空间换时间

A处指如果匹配到第一位是[ ,则说明是数组,接下来用数组的方式处理
B处指如果以L开头和以;结尾,则直接删除这两个字符再loadClass加载
继续跟进:

if (contextClassLoader != null) {
clazz = contextClassLoader.loadClass(className);
mappings.put(className, clazz); // 指放入缓存里面
return clazz;
}
这里获得线程上下文的类加载器 Launcher$AppClassLoader
然后加载传入的org.example.demo.Person类,并放入缓存里面
继续跟进:

这个object就是指传进来的空的键值对,每加载完一轮key-value之后就会向这个object里面放
继续跟进,来到这里:

ObjectDeserializer deserializer = config.getDeserializer(clazz);
return deserializer.deserialze(this, clazz, fieldName);
之前使用它的解析器去解析,是基于字符串的操作,都是解析JSON字符串。
接下来就是解析Java的方法去反序列化(也可以叫做解析JAVA对象)
先通过config.getDeserializer获得反序列化器,然后利用反序列化器去反序列化,这步做完返回的就是Person了
接着跟进这个反序列化器getDeserializer:
来到ParserConfig类的getDeserializer方法:

ObjectDeserializer derializer = this.derializers.get(type);
this.deserializers为缓存,这也是从缓存表里去找
继续跟进,来到getDeserializer类:

跟进getDeserializer类:

继续跟进:

这里
JSONType annotation = clazz.getAnnotation(JSONType.class);
在获得JSONType注解,如果自己写了这个的话相当于写了个反序列化器
继续跟进:

这个denyList是一个黑名单,更多的是考虑性能

这里进行了匹配,为java.awt包中的特定类注册一个自定义的反序列化处理器
之后也是进行类似的匹配,并注册类似的反序列化器

clazz.isEnum()
判断clazz是否是一个枚举类型
然后接着挨个匹配
derializer = createJavaBeanDeserializer(clazz, type);
如果上面都不是,则调用createJavaBeanDeserializer,按照JavaBean解析
跟进createJavaBeanDeserializer方法:

public ObjectDeserializer createJavaBeanDeserializer(Class<?> clazz, Type type) {
boolean asmEnable = this.asmEnable;
if (asmEnable) {
JSONType jsonType = clazz.getAnnotation(JSONType.class);
if (jsonType != null) {
Class<?> deserializerClass = jsonType.deserializer();
if (deserializerClass != Void.class) {
try {
Object deseralizer = deserializerClass.newInstance();
if (deseralizer instanceof ObjectDeserializer) {
return (ObjectDeserializer) deseralizer;
}
} catch (Throwable e) {
// skip
}
}
asmEnable = jsonType.asm();
}
其中,
boolean asmEnable = this.asmEnable;
用于从当前对象中获取ASM是否启用的状态,默认为true
asmEnable在某些情况下是不支持的
比如跟进到这里:

if (!Modifier.isPublic(superClass.getModifiers())) {
asmEnable = false;
break;
}
Modifier.isPublic(superClass.getModifiers())
用于检测一个类superClass是否是public修饰,如果不是的话就使得asmEnable=false,导致加载不了
继续跟进:

JavaBeanInfo beanInfo = JavaBeanInfo.build(clazz, type, propertyNamingStrategy);
这个build方法用于创建类对应的反序列化器时,他要把类里面的东西先了解清楚,如构造函数、Getter、Setter,然后把它组成JavaBeaninfo
跟进build:

public static JavaBeanInfo build(Class<?> clazz, Type type, PropertyNamingStrategy propertyNamingStrategy)
注意用的是JavaBeanInfo这个类
这个函数的实现很重要
第134行:
Field[] declaredFields = clazz.getDeclaredFields();
Method[] methods = clazz.getMethods();
获取所有的字段:

获取所有的方法:

包括Getter、Setter
get
跟进到这里:

for (Method method : methods)
这段在遍历method,目的是获取所有字段
这里的method还包括继承的Object里面的方法
从整体上看:
for (Method method : methods)
for (Field field : clazz.getFields())
for (Method method : clazz.getMethods())
第一次遍历method是找所有的Setter:


if (!(method.getReturnType().equals(Void.TYPE) || method.getReturnType().equals(method.getDeclaringClass()))) {
continue;
}
这里进行了if判断,第一个条件是返回值是void修饰,第二个条件是用于检测一个方法是否返回其自身类型的实例
然后当method为setName的时候:

将前三个字符set设为小写,加上截取的后面的内容
继续跟进:

Field field = TypeUtils.getField(clazz, propertyName, declaredFields);
跟进这个getField:

从所有的field里面找,随机匹配
继续跟进:

找到之后就要创建FieldInfo
跟进这个FieldInfo:

继续跟进,来到getOnly这里:

这个getOnly在之后可能会遇到
关于这儿的add方法:

这个add的作用就是往一个List里面放
第二个遍历所有public变量:
for (Field field : clazz.getFields())
如果没有public修饰的成员变量的话,就会跳过
第三个遍历method是找所有的Getter:

for (Method method : clazz.getMethods()) { // getter methods
String methodName = method.getName();
if (methodName.length() < 4) {
continue;
}
跟进到这里:

if (Collection.class.isAssignableFrom(method.getReturnType()) //
|| Map.class.isAssignableFrom(method.getReturnType()) //
|| AtomicBoolean.class == method.getReturnType() //
|| AtomicInteger.class == method.getReturnType() //
|| AtomicLong.class == method.getReturnType() //
)
只有返回值是Map、AtomicBoolean、AtomicInteger、AtomicLong的时候,才会进入:

就是只有setter,没有getter,才会把它作为单独的方法加入add()
显然不满足,于是进入:

进入JavaBeanInfo的构造方法
继续跟进:

通过刚才的Getter、Setter方法得到两个字段--name和age,同时也得到这些字段对应的setter方法,但是没有getter方法,因为有setter就不会去找gettter,因为反序列化讲究的是赋值,即setter
获取完后还有机会把asmEnable关掉

这个asmEnable有什么用呢?
就是在这里:(继续跟进)

if (!asmEnable) {
return new JavaBeanDeserializer(this, clazz, type);
}
如果asmEnable为false的话,就直接创建一个JavaBeanDeserializer
JavaBeanInfo beanInfo = JavaBeanInfo.build(clazz, type, propertyNamingStrategy);
如果为true的话,就再build一次

return asmFactory.createJavaBeanDeserializer(this, beanInfo);
用的是asmFactory,临时创建一个类进行反序列化
跟进去:

这个会有什么问题呢?
这是一个临时创建的类,我们调试不了

JavaBeanDeserializer是它的父类,我们只能知道它有操作,但是不知道具体干了什么
所以我们要让asmEnable = false
选用的方法是:

让这里的getOnly为true
全局查找getOnly的用法:

现在想的是怎么进到getOnly = true这里

即:
if ((types = method.getParameterTypes()).length == 1)
这行代码获取当前处理的方法的所有参数类型,并将其赋值给变量types
检查方法是否恰好有一个参数,如果是,则执行if块内的代码
显然,我们的目的是进入else块内的代码,所以要让它的参数类型不为1
但是默认来说,进到这里面的,它的流程是把所有满足条件的set方法放进来,但是其中有个条件是:

即长度不为1的话就退出了
所以如果是set方法的话,就不能使得getOnly为true
其中从名字也可以看出,不能有set方法,只能有get方法
如果想要从gettter进来的话,就要让返回值满足这几种:

即:
Map
AtomicBoolean
AtomicInteger
AtomicLong
所以在Person类增加一个属性的get方法,这个方法的返回值是上面四种之一,而且这个属性只有get方法,没有set方法,因为有setter就没有getter
即增加:
import java.util.Map;
private Map map;
public Map getMap(){
return map;
}
即把Person类变为:
package org.example.demo;
import java.util.Map;
public class Person {
private Integer age;
private String name;
private Map map;
public Person() {
System.out.println("constructor");
}
public Person(Integer age, String name) {
this.age = age;
this.name = name;
}
public Integer getAge() {
System.out.println("getAge");
return age;
}
public void setAge(Integer age) {
System.out.println("setAge");
this.age = age;
}
public String getName() {
System.out.println("getName");
return name;
}
public void setName(String name) {
System.out.println("setName");
this.name = name;
}
public Map getMap(){ // 有set就没get,所以这里不能有setMap
return map;
}
}
第二次调试







跟进build,然后一直步过
来到三部分:

三部分:第一个遍历所有settter,第二个遍历所有public字段,第三个遍历所有getter
第一个跑完放了两个setter方法,直接跳到第一个部分的add方法这儿来:

这里循环add两次,第一次是setName,第二次是setAge
来到:
for (Field field : clazz.getFields()) {

由于没有public字段,所以直接跳到第三部分遍历getter方法:

然后又开始循环,直到getMap方法:

继续:

发现:
Map.class.isAssignableFrom(method.getReturnType())
这里的值为true
所以进入这一部分:

然后来到add方法:

步入FieldInfo的构造方法,然后跳到这儿:

由于getMap的参数值是空,即0,所以进入else块:

这里就让getOnly为true了
这样就有一个getOnly = true的 field
接着步出刚才的build:

跳到for循环的这里:

多次循环后到了map,它的getOnly = true:

使得fieldInfo.getOnly = true,进入if块:

这样使得asmEnable = false
接着跳到:

进入了:
return new JavaBeanDeserializer(this, clazz, type);
接着步入到JavaBeanDeserializer里面开始调试:

强制运行到断点这里:

发现deserializer(反序列化构造器)变为了JavaBeanDeserializer
接着步入开始反序列化:

继续步入,来到了这里:

现在捋一下目前做了什么
第一步是parse:

和parseObject:

先按着字符串解析,并且发现有@type的话就按Java对象解析
重点在这里:

对于这个类,我们需要有反序列化器,刚才所做的都是为了把这个反序列化器拿到,然后调用对应的反序列化方法deserialze
关于刚才演示中调用了构造函数、Getter、Setter这件事
首先来到这里:

这个for循环是在遍历所有字段
可以通过查看当前fieldinfo信息获知:

如果有field对应的反序列化器:

接着跳到这儿:

跟进这个createInstance:

如果是接口的话就创造动态代理,如果不是接口的话,就调用构造方法
跟进到这儿:

此时控制台:

调用完构造函数之后就是给他赋值:

跟进这个setValue方法:


method.invoke(object, value);
这里调用invoke方法调用setValue方法

循环调用:

控制台:

然后map那个属性没有,因为在开始赋值的时候没有:

如果有的话,就会调用getMap方法
然后就结束了,来到了这儿:


obj是一个Person对象
那Getter方法在哪儿调用的呢?
实际上在这儿:

toJson把对象转为JSON,调用了Getter
跟进这个toJson,来到了getObjectWriter:

这个getObjectWriter对应之前的deserializer
跳到getFieldValueMap:
在getFieldValueMap里面调用了Getter
跟进getFieldValueMap
跳到getFieldValuesMap:
这个getFieldValuesMap里面调用了Getter
继续跳到这个for循环里面:

跟进getPropertyValue:

继续跟进:

跟进这个get方法:

Object value = method.invoke(javaObject, new Object[0]);
发现这里也是调用了invoke方法,从而调用Getter
跟进这个method.invoke:


这差不多就是整个流程了。。。
fastjson在解析的时候为什么会有代码执行的问题?
因为它一定会调用get和set方法,而且底层都是以invoke的方法调用的
即找一个恶意的set方法,然后把这个类传进去即可
即恶意类Test:
package org.example.demo;
import java.io.IOException;
public class Test {
public void setCmd() throws IOException{
Runtime.getRuntime().exec("calc");
}
}
然后调用:
String s = "{\"@type\":\"org.example.demo.Test\",\"cmd\":\"calc\"}";
JSONObject jsonObject = JSON.parseObject(s);
System.out.println(jsonObject);
constructor
setAge
setName
getAge
getName
{"name":"ttt","age":18}
即可
调试漏洞代码
以以下poc为例:
Test.java
package org.example.fastjson;
import java.io.IOException;
public class Test {
public void setCmd(String cmd) throws IOException{
Runtime.getRuntime().exec(cmd);
}
}
fastjson_poc.java:
package org.example.fastjson;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
public class fastjson_poc {
public static void main(String[] args) throws Exception {
String s = "{\"@type\":\"org.example.fastjson.Test\",\"cmd\":\"calc\"}";
JSONObject jsonObject = JSON.parseObject(s);
System.out.println(jsonObject);
}
}
开始调试:

跟进JSON.parseObject方法:

这里有两个分支
Object obj = parse(text); // 调用Setter、构造函数
return (JSONObject) JSON.toJSON(obj); // 调用Getter
进入JSON.parse:

DefaultJSONParser parser = new DefaultJSONParser(text, ParserConfig.getGlobalInstance(), features); // 创建DefaultJSONParser对象
// 第二个参数为全局的ParseConfig实例
// features为解析器的特性
// 这相当于parser是一个解析器
Object value = parser.parse(); // 获得解析器的parse方法
跟进解析器里面:

public DefaultJSONParser(final String input, final ParserConfig config, int features){
this(input, new JSONScanner(input, features), config);
// this关键字这里用于引用当前对象的构造函数,称为this调用,用于在构造函数中调用同一个类中的另一个构造函数
}

public DefaultJSONParser(final Object input, final JSONLexer lexer, final ParserConfig config){
this.lexer = lexer;
this.input = input;
this.config = config;
this.symbolTable = config.symbolTable;
int ch = lexer.getCurrent();
if (ch == '{') {
lexer.next();
((JSONLexerBase) lexer).token = JSONToken.LBRACE;
} else if (ch == '[') {
lexer.next();
((JSONLexerBase) lexer).token = JSONToken.LBRACKET;
} else {
lexer.nextToken(); // prime the pump
}
}
这里设置解析器的初始状态,并根据JSON文本的起始字符(对象或数组)设置相应token
接着进入DefaultJSONParser.parse方法:

case LBRACE: // 以{ 开头,说明是对象
JSONObject object = new JSONObject(lexer.isEnabled(Feature.OrderedField)); // 获得JSONObject对象
return parseObject(object, fieldName); // 解析对象
来到DefaultJSONParser.parseObject方法:

key = lexer.scanSymbol(symbolTable, '"');
这里把@type读取出来
有@type的话就按JAVA对象解析

if (key == JSON.DEFAULT_TYPE_KEY && !lexer.isEnabled(Feature.DisableSpecialKeyDetect))
Class<?> clazz = TypeUtils.loadClass(typeName, config.getDefaultClassLoader());
ObjectDeserializer deserializer = this.config.getDeserializer(clazz);
第一部分的JSON.DEFAULT_TYPE_KEY:

@type指定为org.example.fastjson.Test类,然后按照org.example.fastjson.Test类去解析、实例化、赋值、调用。
第二部分的loadClass:

ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); // 获得线程上下文的类加载器Launcher$AppClassLoader
if (contextClassLoader != null) {
clazz = contextClassLoader.loadClass(className); // 获得Test类的Class对象
mappings.put(className, clazz); // 将Test类名、Test的Class对象一起放到一个mappings映射中
return clazz; // 返回Class对象
接着进入DefaultJSONParser.parseObject的这里:

ObjectDeserializer deserializer = this.config.getDeserializer(clazz);
这里把Test的Class对象传进去,最终获得反序列化器,即JavaBeanDeserializer类的实例化对象
接下来看怎么获得这个反序列化器的:
跟进ParserConfig.getDeserializer:

由于传入的是Test的Class对象,属于Class<?> 的实例化对象
接着进入另一个getDeserializer方法:

ObjectDeserializer derializer = derializers.get(type);
这里尝试从derializers的映射(Map)中获取与type对应的ObjectDeserializer实例,应该就是尝试获取Test.class的反序列化器
说了半天,反序列化器是什么?
反序列化器就是将数据格式(如JSON、XML)转换回原始的对象或数据结构。例如,将JSON字符串转换为Java对象。
查看derializers的定义:

derializers为IdentityHashMap的实例
继续跟到ParserConfig.getDeserializer的这里:

判断类名是否在黑名单中,防止潜在的安全危险,可能是为了保障线程安全
然后跟进到ParserConfig.getDeserializer的这里:

derializer = createJavaBeanDeserializer(clazz, type);
进行了各种判断,如枚举、数组等类型,显然我们创建的Test类不属于这里的任意一种类型
如果不是的话,则创建按照JavaBean解析的反序列化器,用于处理普通的JavaBean对象
跟进到ParserConfig.createJavaBeanDeserializer方法中:

boolean asmEnable = this.asmEnable;
asmEnable为true时代表启用了ASM(一种Java字节码操作框架),后面要用到
继续跟进到ParserConfig.createJavaBeanDeserializer的这里:

JavaBeanInfo beanInfo = JavaBeanInfo.build(clazz, type, propertyNamingStrategy);
为了创建Test类对应的反序列化器,这里调用JavaBeanInfo.build来把Test类里面的东西先了解清楚,如构造函数、Getter、Setter,然后把这些信息放到beanInfo里面
跟进到JavaBeanInfo.build里面:

Field[] declaredFields = clazz.getDeclaredFields(); // 获取所有的字段
Method[] methods = clazz.getMethods(); // 获取所有的方法
Constructor<?> defaultConstructor = getDefaultConstructor(builderClass == null ? clazz : builderClass); // 获取构造方法
随即就跟进到JavaBeanInfo.build的这三个for循环:

先看第一个
for (Method method : methods)
遍历所有的方法

我们的目的是不进入continue,即A处要求非静态(即不用static修饰),B处要求返回类型为void,C处要求有且只有一个参数

D处要求以set开头的方法名
E和F处看第四个字符是否是大写字母(JavaBean的格式要求)或者其ascii码大于512,如果不是,则先全部小写,再将第四个字符大写,使其满足JavaBean格式的方法的命名要求
然后到第一个for循环的add方法这里:

add(fieldList, new FieldInfo(propertyName, method, field, clazz, type, ordinal, serialzeFeatures, parserFeatures,annotation, fieldAnnotation, null));

将符合的放到fieldList里面
第一个for循环后fieldList列表的值:

只有一个cmd,因为有cmd的setter方法,所以将其放到fieldList之中
进入第二个for循环:
for (Field field : clazz.getFields())

第三个for循环:

显然就是将有getter方法的字段名放到FieldList之中

return new JavaBeanInfo(clazz, builderClass, defaultConstructor, null, null, buildMethod, jsonType, fieldList);
将这个类的内部信息作为JavaBeanInfo的实例返回
接着步出JavaBeanInfo.build方法,来到这里:

if (fieldInfo.getOnly) {
asmEnable = false;
break;
}
这里fieldInfo.getOnly为false,不能进入if代码块,让asmEnable为false
继续跟进到这里:

步出PaserConfig.createJavaBeanDeserializer,得到Test类的反序列化器

接着步出ParserConfig.getDeserializer,来到DefaultJSONParser.parseObject这里:

return deserializer.deserialze(this, clazz, fieldName);
这里调用反序列化器中的deserialze方法进行反序列化
这个反序列化器就是JavaBeanDeserializer类的实例
进入JavaBeanDeserializer.deserialze方法:

继续跟进:
emmm,这里好像就跟进不了了。。

为什么跟进不了?因为在执行PersonConfig.getDeserializer的这里的时候:

return asmFactory.createJavaBeanDeserializer(this, beanInfo);
临时创建一个类进行反序列化,此时的反序列化器是:FastjsonASMDeserializer_1_Test类的实例:

由于这个类是临时创建的,项目中没有这个类,所以无法跟进调试
如果想要调试的话就要加上这一段代码:

private Map map;
public Map getMap(){
return map;
}
加上这个之后反序列化器是JavaBeanDeserializer的实例:

接下来的调试就会进入JavaBeanDeserializer类
跟进到JavaBeanDeserializer.deserialze的这里:

跟进setValue内部:

method.invoke(object, value);
调用invoke方法

相当于执行:
object.setCmd("calc");
也就是调用了这个:

因此步过后就calc了
接着看怎么调用Getter方法的
在上面的基础上继续调试,来到JSON.parseObject的这里:

跟进,来到了JSON.toJSON的这里:

Map<String, Object> values = javaBeanSerializer.getFieldValuesMap(javaObject);
跟进getFieldValuesMap方法内部,来到javaBeanSerializer.getFieldValuesMap:

for (FieldSerializer getter : sortedGetters) {
map.put(getter.fieldInfo.name, getter.getPropertyValue(object));
}
这段代码的目的是遍历sortedGetters结合中所有FieldSerializer对象,使用它们的getPropertyValue方法从传入的object中提取出每个字段的值

跟进getter.getPropertyValue,来到FieldSerializer.getPropertyValue内部:

跟进FieldInfo.get方法:

if (method != null) {
Object value = method.invoke(javaObject, new Object[0]);
return value;
}
发现这里也调用了method.invoke

method.invoke(javaObject, new Object[0]);
相当于执行:
test.getMap();
也就是在这里执行了Test类里的Getter方法
继续跟进来到这里:

这里就开始执行代码了。。
至此,这就是整个流程
如有误,请留言




浙公网安备 33010602011771号