@ResponseBody、@RequestBody是我们开发中常用的注解,因为SpringMVC会扫描该类注解,选择适当的转换器进行数据转换。目前主流的是使用JSON格式数据,因此SpringMVC中有提供 MappingJackson2HttpMessageConverter 转换器,该转换器是我们最常用到的(网上有很多文章,都有进行过源码追踪,有兴趣的读者可以去了解下SpringMVC是如何把请求消息或返回对象进行转换)。
当然在进行文件下载的情况下会使用流传输,这时候是根据请求头或响应头来选择合适的converter,想进一步了解可以参考下这篇文章:https://blog.csdn.net/zbajie001/article/details/79738181。
下面我主要分三点描述:
1.spring MVC 配置自定义转换器
2.spring cloud服务调用Feign的Decoder、Encoder配置自定义转换器
3.redisTemplate 实现对Geometry的序列化
下面例子主要是介绍场景供参考,大家可以参考下面的思路来解决问题
1.spring MVC 配置自定义转换器
我目前遇到的情况是java代码里我用到了
com.vividsolutions.jts.geom.Geometry;
该类是图形数据对象,无法序列化为JSON,必须要依靠自定义的序列化方式,后来我在网上扒到了一个开源项目,专门处理图形对象的序列化,附上链接:https://github.com/bedatadriven/jackson-datatype-jts
结合jackson的ObjectMapper来使用,MappingJackson2HttpMessageConverter转换器也正是使用ObjectMapper来序列化的。
/**
* @author chenws
*/
@Configuration
@EnableWebMvc
@Slf4j
public class WebConfiguration implements WebMvcConfigurer {
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
//初始化MappingJackson2HttpMessageConverter,注册自定义模块
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder().modules(new JtsModule());
MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter = new MappingJackson2HttpMessageConverter(builder.build());
ByteArrayHttpMessageConverter byteArrayHttpMessageConverter = new ByteArrayHttpMessageConverter();
StringHttpMessageConverter stringHttpMessageConverter = new StringHttpMessageConverter();
ResourceHttpMessageConverter resourceHttpMessageConverter = new ResourceHttpMessageConverter();
ResourceRegionHttpMessageConverter resourceRegionHttpMessageConverter = new ResourceRegionHttpMessageConverter();
SourceHttpMessageConverter sourceHttpMessageConverter = new SourceHttpMessageConverter();
AllEncompassingFormHttpMessageConverter allEncompassingFormHttpMessageConverter = new AllEncompassingFormHttpMessageConverter();
Jaxb2RootElementHttpMessageConverter jaxb2RootElementHttpMessageConverter = new Jaxb2RootElementHttpMessageConverter();
//转换器,根据实际情况添加或减少
//此处的mappingJackson2HttpMessageConverter就是我们自定义的转换器,其实核心就是
//初始化的时候注册了JtsModule,支持Geometry的序列化
converters.add(mappingJackson2HttpMessageConverter);
converters.add(stringHttpMessageConverter);
converters.add(resourceHttpMessageConverter);
converters.add(byteArrayHttpMessageConverter);
converters.add(resourceRegionHttpMessageConverter);
converters.add(sourceHttpMessageConverter);
converters.add(allEncompassingFormHttpMessageConverter);
converters.add(jaxb2RootElementHttpMessageConverter);
}
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/");
registry.addResourceHandler("/templates/**").addResourceLocations("classpath:/templates/");
// 放行swagger
registry.addResourceHandler("swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
}
}
大家也可以参考JtsModule来编写自己的序列化工具,网上也很多教程。
简单的一个配置类,就可以完成对Geometry类的JSON字符串转换。
2.spring cloud服务调用Feign的Decoder、Encoder配置自定义转换器
第一点解决的只是出口的序列化,现在springcloud成为微服务主流,服务间调用时不可避免的,因此问题是进行服务调用的时候我们也需要对解码编码下功夫,让他支持Geometry的序列化和反序列化。
同样我们也需要一个配置文件(Feign)
/**
* @author chenws
*/
@Configuration
@Slf4j
public class FeignConfig {
@Autowired
private ObjectFactory<HttpMessageConverters> messageConverters;
@Bean
public Encoder feignFormEncoder() {
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder().modules(new JtsModule());
ObjectFactory<HttpMessageConverters> objectFactory = () -> new HttpMessageConverters(new MappingJackson2HttpMessageConverter(builder.build()));
return new SpringFormEncoder(new SpringEncoder(objectFactory));
}
@Bean
public Decoder feignDecoder() {
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder().modules(new JtsModule());
ObjectFactory<HttpMessageConverters> objectFactory = () -> new HttpMessageConverters(new MappingJackson2HttpMessageConverter(builder.build()));
return new ResponseEntityDecoder(new SpringDecoder(objectFactory));
}
}
此配置也是一个全局配置,同样的道理,自定义MappingJackson2HttpMessageConverter,支持Geometry
3.redisTemplate 实现对Geometry的序列化
RedisTemplate我也还是使用了jackson自带的支持redis序列化类Jackson2JsonRedisSerializer,直接看代码
/**
* @author chenws
*/
@Configuration
public class RedisTemplateConfig {
@Bean
public RedisTemplate redisTemplate(RedisConnectionFactory connectionFactory) {
RedisTemplate template = new RedisTemplate();
template.setConnectionFactory(connectionFactory);
//使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值(默认使用JDK的序列化方式)
Jackson2JsonRedisSerializer serializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,false);
mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
//CustomJtsModule
mapper.registerModule(new CustomJtsModule());
serializer.setObjectMapper(mapper);
template.setValueSerializer(serializer);
//使用StringRedisSerializer来序列化和反序列化redis的key值
template.setKeySerializer(new StringRedisSerializer());
template.afterPropertiesSet();
return template;
}
}
重点是在mapper.registerModule(new CustomJtsModule()),这里其实原理跟上面的都一样,都是调用ObjectMapper来进行序列化和反序列化,此处需要注意的是,我自定义了一个CustomJtsModule,因为我ObjectMapper序列化的时候使用了数据类型和数据绑定,
mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
开源的jackson-datatype-jts 的GeometrySerializer会有问题,它少实现了一个方法serializeWithType。
结束
本文主要是解决了在三种情况下,特殊字段无法序列化的问题,其实实现原理都是一样的。我们可以参考用法,实现自己的序列化方式,当然我们也不一定用Jackson,也可以用阿里的fastjson,反正就根据我们的实际情况,来自定义转换器或redis序列化方式。
如需转载请标明出处:https://blog.csdn.net/shuoshuo132