用自定义注解实现fastjson序列化的扩展

  这篇文章起源于项目中一个特殊的需求。因为目前的开发方式是先后端分离的,基本上是经过接口提供各个服务。前端

  而前两天前端fe在开发中遇到了一些问题:他们在处理字符串类型的时间时会出现精度丢失的状况,因此但愿后台是以时间戳的形式返回给前端。而与此同时后台的设计是这个样子的:全部的时间在数据库中均保存为varchar类型,在序列化的时候也是按String字符串去处理的。java

 

  这样一来就须要一些解决方案:
数据库

  1. 全部数据库的时间字段都用timestamp替换,这个是最简单确实实现代价最高的一种方案,因为数据库表太多而且涉及多处的耦合,此方案不可行。json

  2. 经过fastjson序列化层去转换类型:首先想到的是能不能经过fastjson本身提供的注解方式实现,后调研以后发现,目前使用的版本并不支持;而后想到能够经过fastjson提供的序列化器重载实现String类型的拦截并作处理。可是这种方案会把全部String的字段都执行这段逻辑,而且还要经过固定的format肯定哪些是日期,这里很是影响性能;最后决定添加自定义注解,在须要这种类型转换的字段上加上自定义的@StringToDate注解,而后在序列化执行的入口,给全部加了此注解的类提供继承并实现扩展的序列化器,完成自定义的序列化过程。后端

  下面就详细说一下这个过程,一是对fastjson源码作一个解析和说明,二也是对本身的工做作一个总结。缓存

1. 如何实现扩展前后端分离

首先来看一下自定义的注解,很简单:工具

  

  关于这个注解的用法和定义,我就不细说了,Retention指定了他的做用域,而Target说明该注解用于field字段上。性能

而后既然是要扩展fastjson的功能,咱们就来看一下fastjson的入口,首先是WebMvcConfigurerAdapter这个类,咱们能够经过继承和重写该类的方法实现MVC的配置,好比拦截器、资源处理器等。咱们重写的方法是configureMessageConverters,这个是用来配置信息转化的converter:学习

能够看到,咱们经过这个方法,调用了父类,同时加入了fastJson的converter,而后咱们来看看FastJsonMessageConverter这个咱们本身写的类:

只是很简单的经过继承实现了自定义config的注入,而后再来看看咱们本身写的这个config:

这里是最关键的地方,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还真的厉害啊。做为一个学习者,只是但愿有朝一日也能写出这么漂亮的代码吧,但愿本身能够不断努力!

相关文章
相关标签/搜索