基于Dubbo的GenericService远程调用下自定义消息转换器FastJsonHttpMessageConverter中FastJson注解失效的问题
公司为了减少代码执行,使用Dubbo的GenericService作为网关屏蔽了SpringMVC的Controller,利用HandlerMapping接口自定义了映射器,利用HandlerAdapter接口自定义了处理器执行器,然后自定义了处理器和消息转换器,在处理器中使用Dubbo的GenericService根据请求路径调用的远程不同的接口方法作为服务层。然后在消息转换器中使用FastJsonHttpMessageConverter将Json格式的消息转换为响应数据。
在大致理解了架构概述之后我们来搬上此次遇到的问题,在服务层的接口的响应类中,为了迎合业务需求,需要将一些字段屏蔽掉,不对外暴露,于是使用了FastJson的@JSONField(serialize = false)注解屏蔽不暴露的字段,乍一看没有问题,但是当我把项目跑起来自测的时候,发现被屏蔽的字段依然出现在响应数据中,注解没有起到作用,难道我写错注解了?于是我在服务层代码中使用JSONObject.toJSONString()将响应对象转换为字符串打印了一下,发现注解有效,没有问题,那就断点跑一下吧,一直跟踪到FastJsonHttpMessageConverter(网关层)源码。
在官方源码中发现它调用了JSON.writeJSONString方法,继续向下跟踪JSON源码。
因为object是要序列化的对象,整个方法中只有这里用到了object对象,所以序列化逻辑肯定在这个方法中,继续向下扒拉。
clazz出来了,一般注解都是通过反射来获取的,大概率是这里(看方法名也很像)。
一直追到这里,具体的逻辑我就不解释了,网上能搜到,大概解释一下,这个方法就是根据class类型来匹配序列化对象,有Map、List、Array、枚举等等反正好多基本类型的匹配,最后匹配不到的就创建JavaBeanSerializer作为序列化对象。然后继续追。
到这里就差不多了,TypeUtils.buildBeanInfo方法就是反射获取各种注解然后对要序列化的字段进行过滤排序等等,生成序列化规则的,方法里面调用了computeGettersWithFieldBase和computeGetters这两个方法,有兴趣的可以跟进去看看。
然后createJavaBeanSerializer方法就是根据规则生成序列化处理对象里面涉及到ASM还是JavaBean两种方式,区别可以自行百度。反正只要创建的序列化处理对象是JavaBeanSerializer不论哪种方式创建基本上注解都能起作用。所以问题就出来了,之所以我在服务层写的注解没起作用,就是因为在消息转换时匹配的序列化处理对象不是JavaBeanSerializer,因为FastJsonHttpMessageConverter是按照序列化对象object来匹配序列化处理对象的,所以我再次回到前面发现Dubbo的GenericService远程调用返回的并不是调用的方法的返回值对象类型,而是统一转换成Map或List等类型,具体为什么这样,自行百度。所以问题就很清楚了,远程调用返回的是Map类型,所以在FastJsonHttpMessageConverter序列化时匹配的序列化处理对象不是JavaBeanSerializer,所以注解就没有起作用。
ps:Dubbo的GenericService远程调用转为Map还有一个坑,对于响应数据中字段为LocalDateTime格式的并不是格式化为时间日期的字符串,而是把LocalDateTime当成Map来处理,所以前端获取的数据并不是我们想要的2021-12-30T10:05:27.162格式而是如下所示:
具体解决方法见:https://blog.csdn.net/qq_40756113/article/details/122230772