框架BigDecimal序列化策略修复(Fastjson源码解析)

目录

前言

自定义序列化策略是如何实现的

我的排查过程

解决方案

小结


前言

我们公司的框架对接口返回的java对象转换为字符串的序列化策略上进行了定制。如:当String类型属性值为null时,会转换为空字符串返回、当List类型为nul时返回长度为0的空数组、当Boolean类型为null时返回false、对Bigdecimal类型的属性值,默认保留2位小数点

关于最后一点,当业务需要指定其他精度时,可以在成员属性上添加@JSONField注解来指定保留小数位数。

    /**
     * 保留小数点后6位
     */
    @ApiModelProperty("经度")
    @JSONField(format = "#0.000000")
    private BigDecimal longitude;

但是经实验后发现,该注解在微服务A下生效,在微服务B下无效,即 始终返回2位小数。因此尝试定位原因并修复。

自定义序列化策略是如何实现的

注:以下代码、图片均为个人工程的模拟重现,去除了一些无关代码以便于理解。

根据框架配置可以发现在WebMvcConfiguration.java中重写了 configureMessageConverters(List<HttpMessageConverter<?>> converters) 方法,并引入了 CustomBigDecimalCodec.java 对BigDecimal属性的序列化策略进行定制。

CustomBigDecimalCodec.java 是一个自定义编解码器,在参数传入、返回时进行 “字符串和BigDecimal属性”之间的转换策略。

它继承了fastjson(version: 1.2.70)的BigDecimalCodec.java并重写了write(JSONSerializer serializer, Object object, Object fieldName, Type fieldType, int features)方法,以实现默认情况下返回2位小数。

同时实现了ContextObjectSerializer.java接口并实现了write(JSONSerializer serializer, Object object, BeanContext context)方法,以实现在有@JSONField(format="xxx")注解时返回自定义精度。

 断点调试发现,微服务A的接口返回BigDecimal属性时,会进入CustomBigDecimalCodec.java类(带@JSONField注解进入第一个方法,不带进入第二个方法)。但是微服务B相同场景下,不会进入CustomBigDecimalCodec.java类,我以此为切入点,进行问题排查。

我的排查过程

我在2个微服务下均调用相同接口,返回包含BigDecimal类型属性的对象,调试对比。

微服务A:

在CustomBigDecimalCodec#write 处打断点,并观察方法调用栈,发现是由com.alibaba.fastjson.serializer.JavaBeanSerializer的470行进入的

 微服务B:

而在微服务B下,并不会进入470行,发现是因为在第307行时,BigDecimal类型属性的值发生了改变,被格式化为了2位小数(而不是像微服务A一样在470行进入CustomBigDecimalCodec.java被格式化)

进入307行的this.processValue方法,发现SerializeFilterable.java的233行,在butler服务下,jsonBeanDeser.valueFilters里存在一个叫做BigDecimalValueFilter的过滤器

这个BigDecimalValueFilter 过滤器实现了ValueFilter接口的process方法,入参为待序列化对象实例、待序列化属性名、待序列化属性值。在方法实现中,它将BigDecimal类型属性值格式化为2位小数(无论是否有@JSONField注解

调试至此,基本上可以得出结论:在微服务B下,存在一个特殊的过滤器BigDecimalValueFilter.java,它的优先级高于CustomBigDecimalCodec.java,且不支持@JSONField注解自定义格式化。导致CustomBigDecimalCodec编解码器失效,即 @JSONField失效。

搜索全局代码,发现在微服务B的WebMvcConfiguration.java 的 configureMessageConverters(List<HttpMessageConverter<?>> converters) 方法下,配置了BigDecimalValueFilter,导致问题出现。

解决方案

1、在WebMvcConfiguration.java中删除BigDecimalValueFilter配置。因为他会覆盖CustomBigDecimalCodec编解码器,导致@JSONField注解自定义BigDecimal精度失效。且CustomBigDecimalCodec编解码器的功能要强于BigDecimalValueFilter过滤器

2、对BigDecimalValueFilter进行改造,使其支持@JSONField注解。刚才提到它实现了process方法,入参中有待序列化对象实例,可以由此向上拿到Class对象,进一步拿到类属性以及属性注解,从而支持@JSONField注解(甚至任何自定义注解

小结

完成Fastjson序列化的源码追踪后,再对比框架中的配置,可以得到如下结论。

1、在 FastJsonHttpMessageConverter.fastJsonConfig.serializeConfig 中,我们可以按照数据类型配置自定义编解码(序列化、反序列化)策略。如:返回BigDecimal类型属性时,固定保留2位精度或自定义精度...

2、在 FastJsonHttpMessageConverter.fastJsonConfig.serializeFilters 中,我们可以配置多个过滤器,它同样能实现上述功能,自由度更高(可拿到待序列化对象实例),且优先级高于自定义编解码器。若过滤器对待序列化属性值造成了变化,则自定义编解码器会对该属性失效

附一个流程图

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
BigDecimalJava 中用于高精度数值计算的类,它的源码相对较为复杂。下面是对 BigDecimal源码进行简要解析: 1. 数据结构:BigDecimal 内部使用一个 int 类型的数组来存储数值的每个位。数组中的每个元素对应一段十进制数的位数,默认每个元素存储 9 位或 18 位。同时,还使用一个 int 类型的标志位来表示数值的正负。 2. 构造方法:BigDecimal 提供了多个构造方法用于创建 BigDecimal 对象。可以通过传入整数、浮点数、字符串等不同类型的参数来创建 BigDecimal 对象。 3. 运算方法:BigDecimal 提供了一系列精确的运算方法,包括加法、减法、乘法、除法等。这些方法通过调用内部的原子操作方法来完成具体的运算,保证了计算结果的精确性。 4. 精度控制:BigDecimal 提供了 setScale() 方法用于设置小数点后保留的位数,并可以选择不同的舍入规则,如四舍五入、向上取整等。这样可以灵活地控制计算结果的精度。 5. 内部操作方法:BigDecimal 内部定义了一系列的原子操作方法,用于执行具体的运算操作,如加法、减法、乘法等。这些方法会根据操作数的精度进行位数调整和对齐,保证运算的精确性。 6. 其他功能方法:BigDecimal 还提供了一些其他功能方法,如取绝对值、取反、取整等。这些方法可以方便地对 BigDecimal 对象进行操作和处理。 总的来说,BigDecimal源码实现了高精度的数值计算,通过使用内部的数据结构和原子操作方法,以及提供精度控制和其他功能方法,保证了计算结果的准确性和精确性。这使得 BigDecimal 成为处理需要高精度计算的场景中的常用工具类。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值