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

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


前言

实际项目场景中想使用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;
    }

调用方法
在这里插入图片描述
解决问题!
在这里插入图片描述

总结

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值