Json   发布时间:2022-04-22  发布网站:大佬教程  code.js-code.com
大佬教程收集整理的这篇文章主要介绍了alibaba fastjson(json序列化器)序列化部分源码解析-2-性能优化B大佬教程大佬觉得挺不错的,现在分享给大家,也给大家做个参考。

前面讲了进行对象解析的两个方面,并讲了针对outWriter将不同类型的数据信息写到buf字符数组。本篇讲解对象解析的过程,即如何将不同类型的对象解析成outWriter所需要的序列信息。并虑其中的性能优化。

取得解析器
首先我们需要取得指定对象的json序列化器,以便使用特定的序列化器来序列化对象。因此,需要有一个方法来取得相对应的序列化器。在fastjson中,使用了一个类似map的结构来保存对象类型和及对应的解析器。对于对象类型,在整个fastjson中,分为以下几类:

1基本类型以及其包装类型,字符串
2基本类型数组以及包装类型数组
3Atomic类型
4JMX类型
5集合类型以及子类
6时间类型
7json类型
8对象数组类型
9javaBean类型

对于第1,2,3,4类型,在fastjson中使用了一个全局的单态实例来保存相对应的解析器;第5类型,处理集合类型,对于集合类型及其,由于其处理逻辑均是一样,所以只需要针对子类作一些的处理,让其返回相对应的集合类型解析器即可;第6类型,时间处理器,将时间转化为类似yyyy-MM-ddTHH:mm:ss.SSS的格式;第7类型,处理fastjson专有jsonAwre类型;第8类型,处理对象的数组形式,即处理数组时,需要虑数组中的统一对象类型;第9,即处理我们最常使用的对象,javaBean类型,这也是在项目中解析得最多的类型。

我们要看一下相对应的取解析器的方法,即类Jsonserializer.getObjectWriter(Class<?> clazz)方法,参其中的实现:

Java代码
  1. publicObjectserializergetObjectWriter(Class<?>clazz){
  2. Objectserializerwriter=mapping.get(clazz);
  3. if(writer==null){
  4. if(Map.class.isAssignableFrom(clazz)){
  5. @H_731_35@mapping.put(clazz,Mapserializer.instancE);
  6. }elseif(List.class.isAssignableFrom(clazz)){
  7. @H_731_35@mapping.put(clazz,Listserializer.instancE);
  8. }elseif(Collection.class.isAssignableFrom(clazz)){
  9. @H_731_35@mapping.put(clazz,Collectionserializer.instancE);
  10. }elseif(Date.class.isAssignableFrom(clazz)){
  11. @H_731_35@mapping.put(clazz,Dateserializer.instancE);
  12. }elseif(JSONAware.class.isAssignableFrom(clazz)){
  13. @H_731_35@mapping.put(clazz,JSONAwareserializer.instancE);
  14. }elseif(JSONStreamAware.class.isAssignableFrom(clazz)){
  15. @H_731_35@mapping.put(clazz,JSONStreamAwareserializer.instancE);
  16. }elseif(clazz.isEnum()){
  17. @H_731_35@mapping.put(clazz,Enumserializer.instancE);
  18. }elseif(clazz.isArray()){
  19. Class<?>componentType=clazz.getComponentType();
  20. ObjectserializercompObjectserializer=getObjectWriter(componentTypE);
  21. @H_731_35@mapping.put(clazz,newArrayserializer(compObjectserializer));
  22. }elseif(Throwable.class.isAssignableFrom(clazz)){
  23. @H_731_35@mapping.put(clazz,newExceptionserializer(clazz));
  24. }else{
  25. @H_731_35@mapping.put(clazz,newJavaBeanserializer(clazz));
  26. }
  27. writer=mapping.get(clazz);
  28. }
  29. returnwriter;
  30. }

首先,取对象解析器是由一个类型为JSONserializerMap的对象mapping中取。之所以使用此类型,这是性能优化的一部分,此类型并不是一个完整的map类型,只是实现了一个类似map的操作的类型。其内部并没有使用基于equals的比较方式,而是使用了Sy@L_696_13@.identityHashCode的实现。对于一个对象,其identityHashCode的值是定值,而对于一个类型,在整个jvm中只有一个,因此这里使用基于class的identityHashCode是可以了,也避免了使用class的euqlas来进行比较。
接着再根据每一个类型从mapping中取出相对应的解析器。首先从继承而来的全局解析器取得解析器,如果对象不属于第1,4类型,而开始进入以下的if else阶段。
我们从上面的源码中,可以看出解析器主要分为两个部分,一个是与解析类型相关的,一个是无关的。比如对于第5,6,7类型,其中最5类型是集合类型,由于不知道集合类型中的具体类型(因为存在继承关系),所以类型无关。对于第8,9类型,其中第8类型为对象数组类型,对于对象数组,数组中的每一个对象的类型都是确定的,且整个数组只有一种类型,因此可以确定其类型,这时候就要使用类型相关解析器了,对于第9类型,需要使用解析对象的类型来确定相对应的javaBean属性,因此是类型相关。
另外一个确定解析器的过程当中,使用了映射机制,即将当前解析器与对应的类型映射起来,以便下一次时使用。对于集合类型及子类型,由于当前类型并不是确定的List或Collection类型,因此将当前类型与集合解析器映射起来。对于对象类型,需要将当前类型传递给相对应的解析器,以确定具体的属性

解析过程

解析方法由统一的接口所定义,每个不同的解析器只需要实际这个方法并提供各自的实现即可。方法为write(JSONserializer serializer,Object object),由Objectserializer提供。带两个参数,第一个参数,即为解析的起点类jsonserializer,此类封装了我们所需要的outWriter类,需要时只需要从此类取出即可。第二个参数为我们所要解析的对象,在各个子类进行实现时,可将此对象通过强制类型转换,转换为所需要的类型即可。
具体的解析过程根据不同的数据类型不所不同,对于第1,2类型,由于在outWriter中均有相对应的方法,所以在具体实现时,只需要调用相应的outWriter方法即可,而不需要作额外的工作。比如对于字符串解析器,它的解析过程如下所示:

Java代码
  1. serializeWriterout=serializer.getWrier();
  2. stringvalue=(String)object;
  3. if(serializer.isEnabled(serializerFeature.UseSingleQuotes)){
  4. out.writeStringWithSingleQuote(value);
  5. }else{
  6. out.writeStringWithDoubleQuote(value);
  7. }

即首先,取得输出时所需要的outWriter,然后再根据配置决定是输出单引号+字符串还是双引号+字符串。

而对于其它并没有由outWriter所直接支持的类型而言,解析过程就相对于复杂一些。但是总的逻辑还是基于以下逻辑:

  1. 基于数据类型特点输出所特有的字符包装内容
  2. 基于数据类型特点转换为outWriter所能识别的内容
  3. 逐步解析,将对象解析产生的字符数组输出到outWriter中

只需此三步,即可完美的解析一个对象。因此,我们可以参其中的一个具体实现,并讨论其中的具体优化措施。
在取得解析器方法getObjectWriter(Class<?> clazz)中,我们可以看到,对于集合类型中的Collection和List,fastjson是分开进行解析的。即两者在解析时在细微处有着不一样的实现。两者的区别在于List是有序的,可以根据下标对元素进行访问,对于常用List实现,ArrayList,使用下标访问子元素的价格为O1。这就是在fastJson中采取的一点优化措施,详细看以下实现代码

Java代码
  1. publicfinalvoidwrite(JSONserializerserializer,Objectobject)throwsIOException{
  2. serializeWriterout=serializer.getWrier();//取得输出
  3. List<?>list=(List<?>)object;//强制转换为所需类型
  4. finalintsize=list.size();
  5. intend=size-1;//此处定义为size-1,是因为对最后一位有特殊处理
  6. //空集合判断,省略之
  7. out.append('[');//集合前缀包装
  8. /**以下代码使用get(X)方法访问下标,实现代码对于ArrayList实现有好处,对于LinkedList是否有好处,还待虑*/
  9. for(inti=0;i<end;++i){
  10. Objectitem=list.get(i);
  11. //空值判断
  12. Class<?>clazz=item.getClass();
  13. if(clazz==Integer.class){//针对Integer.class特殊优化,使用outWriter自带方法
  14. out.writeIntAndChar(((Integer)item).intValue(),',');
  15. }elseif(clazz==Long.class){//针对Long.class特殊优化,使用outWriter自带方法
  16. longval=((Long)item).longValue();
  17. out.writeLongAndChar(val,');
  • }else{
  • serializer.write(item);//递归调用,写集合内元素
  • out.append(',');//间隔符
  • }
  • }
  • /**以下代码为最后一位优化,当为最后一位时,不再需要输出间隔符,而是输出后缀包装符
  • 这里即在处理时,直接输出后缀,与前面输出间隔符相对应*/
  • Objectitem=list.get(end);
  • Class<?>clazz=item.getClass();
  • if(clazz==Integer.class){
  • out.writeIntAndChar(((Integer)item).intValue(),']');
  • }elseif(clazz==Long.class){
  • out.writeLongAndChar(((Long)item).longValue(),']');
  • }else{
  • serializer.write(item);
  • out.append(']');
  • }
  • }
  • 以下实现与collection相比不同的即在于处理中间元素与末尾元素的区别。相对于Collection,就不能使用以上的方法了,在实现上,就只能先输出前缀,再一个一个地处理里面的元素,最后输出后缀。此实现如下所示:

    Java代码
    1. publicvoidwrite(JSONserializerserializer,Objectobject)throwsIOException{
    2. serializeWriterout=serializer.getWrier();
    3. Collection<?>collection=(Collection<?>)object;
    4. out.append('[');
    5. Booleanfirst=true;
    6. for(Objectitem:collection){
    7. if(!first){out.append(',');}
    8. first=false;
    9. Class<?>clazz=item.getClass();
    10. //Integer.class和Long.class特殊处理
    11. serializer.write(item);
    12. }
    13. out.append(']');
    14. }

    以上代码就是通常最常见的实现了。

    相对于集合类型实现,map实现和javaBean实现相对来说,稍微复杂了一点。主要是输出key和value的问题。在fastjson中,key输出表现为使用outWriter的writeKey来进行输出,value输出则同样使用常规的输出。按照我们先前所说的逻辑,首先还是输出包装字符内容,即{,在末尾输出}。然后,再根据每个key-value映射特点,采取相对应的输出方式。
    当然,对于map类型输出和javaBean输出还是不一样的。两者可以互相转换,但fastjson在输出时还是采取了不一样的输出方式。那么,我们以源代码来查看相应的实现:

    Java代码
    1. publicvoidwrite(JSONserializerserializer,Objectobject)throwsIOException{
    2. serializeWriterout=serializer.getWrier();
    3. @H_731_35@map<?,?>map=(Map<?,?>)object;
    4. out.write('{');//前缀
    5. Class<?>preClazz=null;//缓存前一个value类型和相对应的解析器,减少类型判断解析
    6. ObjectserializerpreWriter=null;
    7. Booleanfirst=true;
    8. for(Map.Entry<?,?>entry:map.entrySet()){
    9. //此处有删除,即根据nameFilter和valueFilter针对key-value作转换处理
    10. if(!first){out.write(',');}//输出间隔符
    11. serializer.writeFieldName(key);//输出字段名+冒号
    12. first=false;
    13. Class<?>clazz=value.getClass();
    14. if(clazz==preClazz){//此处即细节优化内容,直接使用前一个解析器,避免再次从jsonserializer中查找
    15. preWriter.write(serializer,value);
    16. }else{
    17. /**此处则就需要从jsonserializer中查找解析器,并输出了*/
    18. preClazz=clazz;
    19. preWriter=serializer.getObjectWriter(clazz);
    20. preWriter.write(serializer,value);
    21. }
    22. }
    23. out.write('}');//后缀
    24. }

    由上可以看出,map的实现还是按照我们常用的思路在进行。在性能优化处,采取了两个优化点,一是输出字段时,并不直接输出字段名,而是采取字段+冒号的方式一起输出,减少outWriter扩容计算。二是在查找value解析器时,尽量使用前一个value的解析器,避免重复查找。
    相比map,javaBean的实现就相对更复杂。javaBean输出并不是采取key-value的方式,而是采取类似fieldserializer的输出方式,即将属性名和值,组合成一个字段,一起进行输出。当然输出时还是先输出字段名,再输出值的实现。那么对于javaBean实现,首先要取得当前对象类型的所有可以输出的类型。
    在fastjson实现中,并没有采取javaBean属性的读取方式,而是采取了使用getXXX和isXXX方法的读取模式,来取得一个类型的可读取属性。作为性能优化的一部分,读取属性的操作结果被缓存到了getter缓存器中。其实,并不是缓存到了getter缓存器中,只是该类型的javaBean序列化器对象被缓存到了jsonserializer的对象类型-序列化器映射中。对于同一个类型,就不需要再次解析该类型的属性了。
    有了相对应的字段,那么在实现时,就按照相应的字段进行输出即可。以下为实现代码

    Java代码
    1. publicvoidwrite(JSONserializerserializer,Objectobject)throwsIOException{
    2. serializeWriterout=serializer.getWrier();
    3. out.append('{');//前缀
    4. for(inti=0;i<getters.length;++i){
    5. Fieldserializergetter=getters[i];//取属性解析器
    6. ObjectpropertyValue=getter.getPropertyValue(object);//取值
    7. //省略中间nameFilter和valueFilter过滤处理
    8. if(commaFlag){out.append(',');}//间隔符
    9. //省略nameFilter和valueFilter过滤之后的输出处理
    10. getter.writeProperty(serializer,propertyvalue);//使用字段解析器输出内容
    11. }
    12. out.append('}');//后缀
    13. }

    由上可见,javaBean的输出实际上和map输出差不多。只不过这里又把属性的解析和输出封装了一层。在使用字段解析器(由Fieldserializer标识)输出字段值时,实际上也是先输出字段名+冒号,再输出字段值。这里就不再详细叙述。

    总结

    在整个解析过程中,更多的是根据对象类型查找到对象解析器,再使用对象解析器序列化对象的过程。在这中间,根据不同的对象采取不同的解析,并在实现中采取部分优化措施,以尽量地提高解析效率,减少中间运算。减少中间运算,是在解析过程中采取的最主要的优化办法。实际上,最主要的优化措施还是体现在outWriter中对于数据的处理上。此处更多的是设计模式的使用,以实现繁多的对象的解析。 整个fastjson的序列化部分,就到此为止。单就笔者而言,在查看源代码的时候,也发现了一些问题,可能是作者未虑的问题,或者是实际中未遇到。但在版本升级过程中,也渐渐地对功能进行了增强,比如对于@JsonField注解的使用,NameFilter和ValueFilter的使用,使fastjson越来越符合业务系统的需要。如果可以,笔者会将其用到笔者所在的项目中,而不再重复发明轮子:)

    本图文内容来源于网友网络收集整理提供,作为学习参使用,版权属于原作者。

    猜你在找的Json相关文章

    @H_333_1247@  jsonp需要在页面中添加一个<script>元素,由该元素来从其他服务器加载json数据。 <body> <script src="js/jsonp.js?callBACk=showEvents"></script>  //从服务器获取的文件,在URL后面加入想要获取的属性 </body>   web浏览器本身需要一个处理json的函数 //这个函数专门用来处理json数据的
    @H_333_1247@<script> var testApi = "地址"; $.ajax({ url:testApi,//可以不是本地域名 type:‘post‘, dataType:‘jsonp‘, //jsonp格式访问 jsonpCallBACk:‘test‘ //获取数据的函数 }) .done(function(data){ if (data.status == 1) { console.log(‘成功‘
    @H_333_1247@最近开发中遇到调用第三方web_api的功能,后端在处理json数据时使用fastjson来做反序列化,由于调用api返回的数据格式主体部分过于繁杂且没有太多可抽象的特征,所以只对头部(返回JSON最外层请求状态部分)进行了简单的分割,之后把剩下的主题内容进行数据库存储操作,并将结果返回给前端,由前端根据不同页面再做解析。 这样做的好处是,如果未来需求有变动(现在看来变动基本没跑了),我们不需要重
    @H_333_1247@JSON全称为JavaScript ObjectNotation,它是一种轻量级的数据交换格式,易于阅读、编写、解析。jsoncpp是c++解析JSON串常用的解析库之一。   jsoncpp中主要的类: Json::Value:可以表示所有支持的类型,如:int , double ,String , object, array等。其包含节点的类型判断(isNull,isBool,isInt,is
    @H_333_1247@var newScript = document.createElement(‘script‘); newScript.setAttribute("src", "http://192.168.255.14:8546/countrys_p.txt"); newScript.setAttribute("type", "text/javascript");
    @H_333_1247@$.ajax({ url:"http://192.168.100.47:8080/ais-connect/data/newaisSyn", type:‘GET‘, dataType:‘JSONP‘, jsonp: ‘callBACk‘,
    @H_333_1247@1.list中放入同一个对象,会出现内存地址引用{"$ref":"#[0]"},后台可以识别,但是前台不会识别 @Test public void testList(){ User user = new User(); user.setUserName("cgx"); user.setpassword("123456");
    @H_333_1247@static class TempClass{ private String name; private int age; public String getName() {return name;} public void setName(String Name) {this.name = name;} public int getAge() {return age;} public void

    alibaba fastjson(json序列化器)序列化部分源码解析-2-性能优化B

    微信公众号搜 "程序精选"关注

    大佬总结

    以上是大佬教程为你收集整理的alibaba fastjson(json序列化器)序列化部分源码解析-2-性能优化B全部内容,希望文章能够帮你解决alibaba fastjson(json序列化器)序列化部分源码解析-2-性能优化B所遇到的程序开发问题。

    如果觉得大佬教程网站内容还不错,欢迎将大佬教程推荐给程序员好友。

    本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
    如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。
    标签:alibababfastjsonjson优化序列性能源码解析部分
    猜你在找的Json相关文章
    其他相关热搜词更多
    phpJavaPython程序员load如何string使用参数jquery开发安装listlinuxiosandroid工具javascriptcap