概述
fastJson的功能是将json字符串转换成一个对象。
转换对象支持用@type
转换成指定类型的对象。
既然有创建对象,必然涉及到类加载、newInstance、属性赋值。
本文记录了我如何分析fj查找对象属性并赋值和调用getter的过程。
分析思路
若直接从入口一步步调试,工作量太大,容易跟不到关键点。
所以先把断点打在sink,直接运行到sink点,此时调用栈显示出来的全都是关键节点,对着这些分析即可。
查看调用栈
版本1.2.22。
byte[] bytes0 = SmallShell.rceHorseCmd("calc"); |
断点先打在getOutputProperties,分析栈。
去到FieldDeserializer#setValue
Method method = fieldInfo.method; |
fieldInfo信息:
所以现在要知道:
1、fieldinfo如何获取到对象的属性的,且他获取的是outputProperties
,实际上应该是_outputProperties
。
2、如何获取到getOutputProperties的
所以应该在调用栈找到最开始出现fieldinfo的地方,在那附近寻找。
获取对象属性
第一处
来到JavaBeanDeserilizer#deserialze,这里也存在fieldinfo。
但是再下一层就不存在了,所以应该从这里开始看。
断点打在这,然后一直跟进
一直来到JavaBeanInfo#build
:
这里面再细看一下,这就是获取上面提到的filedinfo的地方。
JavaBeanInfo#build
内容概括:
1、反射获取所有方法
2、遍历所有方法,获取setter。获取满足规则的setter,必须以set开头,参数只能有1个等等,还有别的规则。
3、对setter处理,若set后面第一个字符是大写,则fieldname就是set后面的单词。
4、setter及其fieldname组成一个FieldInfo,存入JavaBeanInfo对象的fields属性。
5、第二次遍历所有方法,获取getter,getter的返回值得满足:
Collection.class.isAssignableFrom(method.getReturnType()) // |
6、getter方法的JSONField注解的name值是fieldname,若没有注解,则fieldname就是get后面的单词,首字母转小写。
最终返回的beaninfo。
这也解释了为什么TemplatesImpl属性名是_outputProperties
但获取的是outputProperties
此时猜测:
属性赋值就是使用FiledInfo里的name和setter来赋值的。但为什么有getter还不清楚。
此时还有两个问题:
1、这里并没有_bytecodes的FieldInfo,说明还有别的地方获取。
2、这里outputProperties的FieldInfo里的name是outputProperties
,没有下划线,但JSON字符串里的有下划线,两者是如何匹配的?
第二处
首先看第一个。
断点打在这,此时key是_bytecodes
:
跟进parseField一次即可,然后里面的内容概括:
0、这个方法的作用是:获取fieldDeserializer,然后调用fieldDeserializer#parseField,给对象属性赋值。下面是获取fieldDeserializer的过程。
1、执行fieldDeserializer = smartMatch(key);
若返回不是null,则return,否则继续。(这个smartMatch后面还会用到)。
2、反射获取所有属性,根据名字匹配,若匹配到,则将name和Filed对象组成FieldInfo对象,然后生成Deserializeer
再然后就是赋值操作,由于fieldInfo的method为null,就直接反射赋值了。
此时对比最上面的outputProperties,method不是null,但field属性为null,直接调用的method。
第一个问题解决了,看第二个。
属性匹配
断点打在smartMatch,令key为_outputProperties
,然后一直跟进。
smartMatch方法内容概括:
1、首先在this已有的sortedFieldDeserializers里根据key二分查找deserializer。
2、若找不到,则检查key是否以下划线开头,若是,则去掉下划线,再找一次。
所以,_outPutProperties
去掉下划线之后就成功找到了deserializer。
总结
1、fj先通过类的setter和getter获取fieldName和method。fieldName和method组成FieldInfo。
2、在将json字符串里的key转换成对象field的过程中,先去第一步获取到的FieldInfo中查找,若没有,则使用反射进行第二次查找field。同样组成FieldInfo。这里的FieldInfo包含了FieldInfo和Field,method为null。
3、在第二次查找属性的过程中,若key以下划线开头,则会去掉下划线。
4、当属性查找完成,则使用setValue。这里会使用第1、2步获取的FieldInfo,若method为null,则用反射赋值。若不为null,无论是getter还是setter,都会调用method.invoke(obj,Field)
。
5、创建对象,应该只需要setter,为何还要getter,这里不理解。