⽤⾃定义注解实现fastjson序列化的扩展
这篇⽂章起源于项⽬中⼀个特殊的需求。由于⽬前的开发⽅式是前后端分离的,基本上是通过接⼝提供各个服务。
⽽前两天前端fe在开发中遇到了⼀些问题:他们在处理字符串类型的时间时会出现精度丢失的情况,所以希望后台是以时间戳的形式返回给前端。⽽与此同时后台的设计是这个样⼦的:所有的时间在数据库中均保存为varchar类型,在序列化的时候也是按String字符串去处理的。
这样⼀来就需要⼀些解决⽅案:
1. 所有数据库的时间字段都⽤timestamp替换,这个是最简单确实实现代价最⾼的⼀种⽅案,由于数据库表太多并且涉及多处的
耦合,此⽅案不可⾏。
2. 通过fastjson序列化层去转换类型:⾸先想到的是能不能通过fastjson⾃⼰提供的注解⽅式实现,后调研之后发现,⽬前使⽤
的版本并不⽀持;然后想到可以通过fastjson提供的序列化器重载实现String类型的拦截并做处理。但是这
种⽅案会把所有String的字段都执⾏这段逻辑,并且还要通过固定的format确定哪些是⽇期,这⾥⾮常影响性能;最后决定添加⾃定义注解,在需要这种类型转换的字段上加上⾃定义的@StringToDate注解,然后在序列化执⾏的⼊⼝,给所有加了此注解的类提供继承并实现扩展的序列化器,完成⾃定义的序列化过程。
下⾯就详细说⼀下这个过程,⼀是对fastjson源码做⼀个解析和说明,⼆也是对⾃⼰的⼯作做⼀个总结。
1. 如何实现扩展
⾸先来看⼀下⾃定义的注解,很简单:
关于这个注解的⽤法和定义,我就不细说了,Retention指定了他的作⽤域,⽽Target说明该注解⽤于field字段上。
然后既然是要扩展fastjson的功能,我们就来看⼀下fastjson的⼊⼝,⾸先是WebMvcConfigurerAdapter
这个类,我们可以通过继承和重写该类的⽅法实现MVC的配置,⽐如、资源处理器等。我们重写的⽅法是configureMessageConverters,这个是⽤来配置信息转化的converter:
可以看到,我们通过这个⽅法,调⽤了⽗类,同时加⼊了fastJson的converter,然后我们来看看FastJsonMessageConverter这个我们⾃⼰写的类:
只是很简单的通过继承实现了⾃定义config的注⼊,然后再来看看我们⾃⼰写的这个config:
fastjson怎么用这⾥是最关键的地⽅,SerializeConfig提供⼀个⼊⼝⽅法getObjectWriter可以⽤来对传⼊的类型进⾏处理,并为相应的类型设置对应的Serializer序列化器。这⾥可以看到,我通过判断传⼊class的注解判断是不是要处理的类型,如果是需要转换的,就⽤我们写的ExtendJavaBeanSerializer去处理他。(同时可
以看到,这⾥我做了缓存处理,每次的class在处理之后都会通过⽗类的put⽅法放⼊缓存,这样可以⼤⼤减少遍历和判断的次数,提⾼处理性能)
那现在就要看看这个ExtendJavaBeanSerializer做了什么:
这⾥我们继承了JavaBeanSerializer并重写了processValue⽅法。这⾥要说个点:因为每个序列化器都有write⽅法,所以最开始直观的想法是重写这个⽅法实现扩展,但是由于write⽅法很长很长(有250多⾏),作为切⼊点⾮常不⽅便,然后仔细观察源码,发现其中有这么⼀句:
propertyValue = this.processValue(serializer, fieldSerializer.fieldContext, object, fieldInfoName,propertyValue);
这句看上去好像就是给我们处理value值的,再点进去⼀看,processValue居然是个protected⽅法。那正如意!这就是给我们做扩展的⼊⼝,最后便重写了这个⽅法,并在其中实现了StringToDate的转换逻辑。
2. fastjson源码思想
其实呢,到这⾥应该会有⼀个疑问,fastjson提供的预设序列化器有很多很多,看源码发现⼀个包下⾯满满都是。那么我们是如何确定要继承这个JavaBeanSerializer的呢?这就需要看fastjson的源码了,看看它究竟是怎么分配和执⾏这个序列化器的:
⾸先是SerializeConfig这个类:这个类的config中预设了⼤量的类型和序列化器的对应关系,他原有的getObjectWriter⼊⼝是这样的逻辑: 先判断传⼊类是不是config中有的,如果有直接给对应的Serializer,如果没有,⼜会有⼀堆特殊类(⽐如集合,异常,编码等类)的判断,⽽他们也有对应的Serializer。
那如果还没有(⽐如⾃⼰的javabean),在最后会执⾏⼀个:createJavaBeanSerializer(clazz) 的⽅法,
在这个⽅法中,默认会执⾏createASMSerializer⽅法,asm查阅资料会发现是⼀个⽼外写的字节码序列化器,是序列化最底层的⼀个⼯具,很多开源序列化包都是对这个进⾏封装实现的。那我们要继承JavaBeanSerializer这个类并使⽤⾃⼰的,就说明我们不想让他执⾏createASMSerializer这个⽅法,为什么呢?
因为在createASMSerializer⾥⾯的逻辑是:当序列化对象 > 256个时,会创建可继承的JavaBeanSerializer去处理,如果<256,则不会给他任何序列化器,⽽是直接通过反射⽅式在内存中(就是⽣⽣拼出了⼀个序列化过程)实现对这个对象的处理。关键代码如下:
当然,下⾯还有很长很长的内存字节码操作的逻辑,我就不贴出了。我想这可能也是fastjson快的⼀个原
因吧。
写到这⾥,关于对fastjson的扩展就差不多说完了,其实fastjson的源码中其他类中也都有⾃⼰独特的优化⽅式,有些还是挺有意思的。另外呢,我惊奇地发现,fastjson的作者和durid居然是⼀个⼈,这个来⾃阿⾥的wenshao还真的厉害啊。作为⼀个学习者,只是希望有朝⼀⽇也能写出这么漂亮的代码吧,希望⾃⼰可以不断努⼒!
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论