irpas技术客

记录使用fastjson消息转换器遇到的问题和解决方法_欣慰丶缘纷_fastjson转换器

网络 4473

记录使用fastjson消息转换器遇到的问题和解决方法

文章目录 记录使用fastjson消息转换器遇到的问题和解决方法前言一、定义一个简单的fastjson消息转换器二、来需求了1.在某一个字段中指定修改它的类型 三、出bug啦1.fastjson消息转换器失效2.自定义@ConvertField注解失效 总结


前言

实际项目场景中想使用fastjson做消息转换器来解决一些类型转换的问题,随着后面踩到的坑也比较多,还好也是一个个解决了


一、定义一个简单的fastjson消息转换器

自定义一个配置类FastJsonConfig添加到HttpMessageConverters中,可以在配置类里过滤或者修改序列化的内容

@Configuration public class FastJsonMessageConvertConfig { @Bean public HttpMessageConverters fastJsonHttpMessageConverters(){ FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter(); FastJsonConfig fastJsonConfig = new FastJsonConfig(); fastJsonConfig.setSerializerFeatures(SerializerFeature.PrettyFormat); converter.setFastJsonConfig(fastJsonConfig); return new HttpMessageConverters(converter); } } 二、来需求了 1.在某一个字段中指定修改它的类型

通过注解的方式实现

@Inherited @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface ConvertField { Class<?> value() default int.class; }

这里更换了一下方式,通过实现WebMvcConfigurer接口,重载extendMessageConverters方法并把自定义的配置类FastJsonConfig添加到这里面去。通过ValueFilter去修改字段的值

@Slf4j @Configuration public class FastJsonMessageConvertConfig implements WebMvcConfigurer { @Override public void extendMessageConverters(List<HttpMessageConverter<?>> converters) { FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter(); FastJsonConfig fastJsonConfig = new FastJsonConfig(); fastJsonConfig.setSerializerFeatures(SerializerFeature.PrettyFormat); fastJsonConfig.setSerializeFilters((ValueFilter) (object, name, value) -> { try { Field field = object.getClass().getDeclaredField(name); ConvertField annotation = field.getAnnotation(ConvertField.class); if (Objects.isNull(annotation)) { return value; } return Convert.convert(annotation.value(),value); } catch (NoSuchFieldException ignore) { return value; } return value; }); converter.setFastJsonConfig(fastJsonConfig); converters.add(converter); } }

这样就可以任意转换你想要的类型了,比如下面字符串类型的字段在序列化时转为int

三、出bug啦 1.fastjson消息转换器失效

这里排查问题排查了很久。通过断点慢慢去看,发现HttpMessageConverter里添加的消息转换器messageConverters有两个MappingJackson2HttpMessageConverter,在遍历converters时首先经过jackson进行转换就不再走fastjson了,从而导致fastjson中@JSONField失效以及自定义的@ConvertField都失效了

解决方法:

首先通过Iterator方式从converters移除这两个MappingJackson2HttpMessageConverter,(不知道还有没有用处的情况下)也可以利用容器暂存起来,然后把fastjson添加进去,再把暂存的两个MappingJackson2HttpMessageConverter重新添加进去,简单理解就是给jackson和fastjson的执行顺序换了一下

@Override public void extendMessageConverters(List<HttpMessageConverter<?>> converters) { // 首先移除jackson2的影响,用别的容器暂存 Iterator<HttpMessageConverter<?>> iterator = converters.iterator(); Set<HttpMessageConverter<?>> set = new ConcurrentHashSet<>(); while (iterator.hasNext()) { HttpMessageConverter<?> converter = iterator.next(); if (converter instanceof MappingJackson2HttpMessageConverter) { set.add(converter); iterator.remove(); } } FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter(); FastJsonConfig fastJsonConfig = new FastJsonConfig(); fastJsonConfig.setSerializerFeatures(SerializerFeature.PrettyFormat); fastJsonConfig.setSerializeFilters((ValueFilter) (object, name, value) -> { try { Field field = object.getClass().getDeclaredField(name); ConvertField annotation = field.getAnnotation(ConvertField.class); if (Objects.isNull(annotation)) { return value; } return Convert.convert(annotation.value(), value); } catch (NoSuchFieldException ignore) { } return value; }); converter.setFastJsonConfig(fastJsonConfig); // 添加fastjson converters.add(converter); // 从容器中暂存的jackson2放回来 for (HttpMessageConverter<?> temp : set) { if (temp instanceof MappingJackson2HttpMessageConverter) { converters.add(temp); } } }

这样执行顺序就会从fastjson开始

2.自定义@ConvertField注解失效

想法是利用@JSONField修改序列化后的字段名称,利用自定义@ConvertField修改字段类型。

通过第一点修改后fastjson转换器生效,@JSONField注解也生效,但自定义@ConvertField却失效了

请求接口后返回的结果: 可以看出@JSONField注解生效,machineTypeId确实改为machineType了,但是字段类型并没有被修改

又通过打断点慢慢去看,最终发现在ValueFilter的lamda表达式中有个name,这个name对应的是返回实体类对应的属性名称,但此时的属性名称已经通过@JSONField从machineTypeId修改为machineType了,而object存储的实体里对应的还是machineTypeId,字段名没有对应上所以没有类型转换

解决方法:

在name获取对应的(已经从@JSONFiled转换后)的实体类名时再次给它转换一下,让它与object中的实体属性名对应

private static final Map<String, String> map = new HashMap<>(); static { map.put("machineType", "machineTypeId"); } private String convertClassAttributeName(String name) { if (map.containsKey(name)) { return map.get(name); } return name; }

调用方法 解决问题!

总结

如果有更好处理方式的,欢迎各位指出,希望对大家有所帮助!


1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,会注明原创字样,如未注明都非原创,如有侵权请联系删除!;3.作者投稿可能会经我们编辑修改或补充;4.本站不提供任何储存功能只提供收集或者投稿人的网盘链接。

标签: #fastjson转换器