记录使用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) {
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);
converters.add(converter);
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;
}
调用方法 解决问题!
总结
如果有更好处理方式的,欢迎各位指出,希望对大家有所帮助!
|