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

而前两天前端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还真的厉害啊。作为一个学习者,只是希望有朝一日也能写出这么漂亮的代码吧,希望自己可以不断努力!

java 自定义注解 生成json_用自定义注解实现fastjson序列化的扩展相关推荐

  1. java 自定义注解 生成json_Jackson 通过自定义注解来控制json key的格式

    最近我这边有一个需求就是需要把Bean中的某一些特殊字段的值进行替换.而这个替换过程是需要依赖一个第三方的dubbo服务的.为了使得这个转换功能更加的通用,我们采用了下面的方式: client端使用自 ...

  2. java 自定义注解 生成json_SpringBoot:自定义注解实现后台接收Json参数

    0.需求 在实际的开发过程中,服务间调用一般使用Json传参的模式,SpringBoot项目无法使用@RequestParam接收Json传参 只有@RequestBody支持Json,但是每次为了一 ...

  3. java 自定义注解 生成json_Java使用@JsonDeserialize注解实现自定义反序列化器

    @JsonDeserialize注解用于在将JSON反序列化为Java对象时声明自定义反序列化器.我们可以通过使用泛型类型Employee继承 StdDeserializer类来实现自定义反序列化器, ...

  4. java自定义编号生成(支持前缀自定义)

    工作中要求合同编号自动生成 格式 NYZL-001,NYZL-002...在我的StringUtils工具类中新增如下方法,该方法可以自定义前缀以及当前排到数字几了(可从数据库中查询最大的编号+1) ...

  5. 详解Dart中如何通过注解生成代码

    简介:详解dart与java注解生成代码异同点 作者:闲鱼技术-龙湫 1.背景 最近在项目中使用到了Dart中的注解代码生成技术,这跟之前Java中APT+JavaPoet生成代码那套技术还是有一些不 ...

  6. 使用Java反射(Reflect)、自定义注解(Customer Annotation)生成简单SQL语句

    使用Java反射(Reflect).自定义注解(Customer Annotation)生成简单SQL语句 这次给大家介绍一下在Java开发过程中 使用自定义注解开发: 主要知识点:          ...

  7. java 外部覆盖内部配置,Spring 与自定义注解、外部配置化的结合使用

    Spring 与自定义注解.外部配置化的结合使用 一.Java注解的简单介绍 注解,也叫Annotation.标注,是 Java 5 带来的新特性. 可使用范围 类.字段.方法.参数.构造函数.包等, ...

  8. 【Java注解系列】内置注解与AOP实现自定义注解

    Java 注解(Annotation)又称 Java 标注,是 JDK5.0 引入的一种注释机制. Java 语言中的类.方法.变量.参数和包等都可以被标注.和 Javadoc 不同,Java 标注可 ...

  9. java 多重注解_Java注解-元数据、注解分类、内置注解和自定义注解

    大家好,我是乐字节的小乐,上次说过了Java多态的6大特性|乐字节,接下来我们来看看Java编程里的注解. Java注解有以下几个知识点:元数据 注解的分类 内置注解 自定义注解 注解处理器 Serv ...

最新文章

  1. python正规教育机构-长沙python培训机构哪家好
  2. SpringMVC源码分析(4)剖析DispatcherServlet重要组件
  3. 如何判断Javascript对象是否存在
  4. ubuntu18下安装微信
  5. 基于智能计算的降维技术研究与应用
  6. 生产者消费者问题 操作系统
  7. 转转、e代驾发布联合报告:代驾司机买二手手机看重验机、质保
  8. Xmind快捷键笔记
  9. 依赖型的关系建立 - 小技巧
  10. UML类图详解及rose中的画法
  11. 2.11 数值分析: 事后误差估计和迭代改善
  12. 批处理控制VMware Workstation服务
  13. 你的格局决定你的结局
  14. 在阿里云ECS服务器上使用NextCloud+Aria2+AriaNg部署下载站
  15. intellij idea 合并分支到主分支,主分支代码同步到某一分支
  16. 财务学python还是vba_作为一名会计应届生准备投身财务,想多学一门技能 是推荐python还是VBA或是其他什么 求指点一下?...
  17. 行人重识别(21)——行人重识别算法性能指标
  18. java odbc 实现access连接并导出mdb格式文件
  19. element-ui 中的el-table表格默认的“暂无数据”修改
  20. Http--跨域请求

热门文章

  1. asp.net 实现一级域名与二级域名共享COOKIE
  2. sql 查询上个月的数据_数据分析-SQL 进阶篇 多表查询
  3. 如何利用python自动化办公项目_python办公自动化:自动进行word文档处理和排版
  4. JDBC实现从Hive抽取数据导入Oracle
  5. Zookeeper Watch监听
  6. [转载] python[1]-print中的sep、end参数
  7. ruby 集合 分组_在Ruby中打印集合的元素
  8. micropython 蜂鸣器_基于MicroPython的TPYBoard微信远程可燃气体报警器的设计与实现...
  9. 未能成功加载扩展程序_【JAVA虚拟机(JVM)精髓】09-几种不同的类加载器
  10. git rebase用法_Git入门实战