用自定义注解实现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:
这里是最关键的地方,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还真的厉害啊。作为一个学习者,只是希望有朝一日也能写出这么漂亮的代码吧,希望自己可以不断努力!
用自定义注解实现fastjson序列化的扩展的更多相关文章
- springBoot2.0+redis+fastJson+自定义注解实现方法上添加过期时间
springBoot2.0集成redis实例 一.首先引入项目依赖的maven jar包,主要包括 spring-boot-starter-data-redis包,这个再springBoot2.0之前 ...
- Spring MVC中使用FastJson自定义注解
最近在做.net转译成Java.其中遇到一个很蛋疼的问题.以前.net属性名都是首字母大写.造成返回给客户端的JSON字符串属性名称都是首字母大写.为了和前端对接我们以前都是如下图所示做法 publi ...
- 170313、poi:采用自定义注解的方式导入、导出excel(这种方式比较好扩展)
步骤一.自定义注解 步骤二.写Excel泛型工具类 步骤三.在需要导出excel的类属相上加上自定义注解,并设置 步骤四.写service,controller 步骤一:自定义注解 import ja ...
- 自定义注解扩展springMvc的validation注解
文章目录 前言 自定义校验注解 使用 后记 前言 我们都知道 springMvc 的检验框架使用的是 hibernate 的 validator ,检验数据,是有那么一点小爽快: 但是,validat ...
- FastJson序列化时过滤字段(属性)的方法总结
FastJson序列化时(即转成JSON字符串时),可以过滤掉部分字段,或者只保留部分字段,方法有很多,下面举一些常用的方法. 方法一.FastJson的注解 @JSONField(serialize ...
- Springboot+Redisson自定义注解一次解决重复提交问题(含源码)
前言 项目中经常会出现重复提交的问题,而接口幂等性也一直以来是做任何项目都要关注的疑难点,网上可以查到非常多的方案,我归纳了几点如下: 1).数据库层面,对责任字段设置唯一索引,这是最直接有效 ...
- ssm+redis 如何更简洁的利用自定义注解+AOP实现redis缓存
基于 ssm + maven + redis 使用自定义注解 利用aop基于AspectJ方式 实现redis缓存 如何能更简洁的利用aop实现redis缓存,话不多说,上demo 需求: 数据查询时 ...
- Java基础笔记 – Annotation注解的介绍和使用 自定义注解
Java基础笔记 – Annotation注解的介绍和使用 自定义注解 本文由arthinking发表于5年前 | Java基础 | 评论数 7 | 被围观 25,969 views+ 1.Anno ...
- java自定义注解知识实例及SSH框架下,拦截器中无法获得java注解属性值的问题
一.java自定义注解相关知识 注解这东西是java语言本身就带有的功能特点,于struts,hibernate,spring这三个框架无关.使用得当特别方便.基于注解的xml文件配置方式也受到人们的 ...
随机推荐
- 张高兴的 Windows 10 IoT 开发笔记:部署 ASP.NET Core 2 应用
今天是大年初二,都去走亲戚了吧,享受一下这难得的能和亲友相聚的时光.而我就不一样了,今天一回到家就又开始瞎折腾了,哈哈哈. 问题背景 最近花了点时间用 ASP.NET Core 2 写了个个人博客,中 ...
- CentOS 6.5 Web服务器搭建
安装MySQL 首先,进入终端,输入 [root@localhost ~]# yum install mysql mysql-server 即可安装Mysql 按照成功以后,让MySQL随系统启动 [ ...
- CA证书扫盲,https讲解。
很多关于CA证书的讲解. 1.什么是CA证书. 看过一些博客,写的比较形象具体. ◇ 普通的介绍信 想必大伙儿都听说过介绍信的例子吧?假设 A 公司的张三先生要到 B 公司去拜访,但是 B 公司的所有 ...
- URL中特殊符号的处理
问题描述 我们在对接第三方系统的时候通常需要get或post来传输数据,但此时如果参数中存在&% #*!包括空格等特殊符号的时候就无法正常请求具体表现在参数获取不正确或者获取不到参数,甚至有时 ...
- Vue的土著指令和自定义指令
1.土著指令 当我开始学习Vue的时候,看官网的时候看到了"指令"两个字.我愣住了,what?指令是啥啊?后来继续往下看,像这种什么"v-for""v ...
- css页面布局之左侧定宽,右侧自适应
二列布局的特征是侧栏固定宽度,主栏自适应宽度.三列布局的特征是两侧两列固定宽度,中间列自适应宽度. 之所以将二列布局和三列布局写在一起,是因为二列布局可以看做去掉一个侧栏的三列布局,其布局的思想有异曲 ...
- System.in实现数据的键盘输入
System.in The "standard" input stream. This stream is already open and ready to supply inp ...
- myeclispe中向mysql中插入中文数据出现??问题解决办法
或许很多人会出现??这种令人头痛的mysql的中文乱码问题:解决如下: 1.先对于新建的数据库要设置默认的字符集为UTF-8 create database mydb default characte ...
- 1 Python数据类型--
常见的Python数据类型: (1)数值类型:就是平时处理的数字(整数.浮点数) (2)序列类型:有一系列的对象并排或者排列的情况.如字符串(str),列表(list),元组(tuple)等 (3)集 ...
- hdu5296 01字典树
根据二进制建一棵01字典树,每个节点的答案等于左节点0的个数 * 右节点1的个数 * 2,遍历整棵树就能得到答案. AC代码: #include<cstdio> using namespa ...