最近项目组用feign调用远程服务,消费端报了如下一个异常
从异常信息可以得出localdatime反序列化出了异常,而这个异常又是因为jackson无法处理导致。因此我们可以为jackson的ObjectMapper适配一下
1、在pom.xml引入
<dependency> <groupId>com.fasterxml.jackson.datatype</groupId> <artifactId>jackson-datatype-jsr310</artifactId> <version>${jackson.version}</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.datatype</groupId> <artifactId>jackson-datatype-jdk8</artifactId> <version>${jackson.version}</version> </dependency>
注: jackson-datatype-jsr310这是用来支持jsr310规范的时间,jackson-datatype-jdk8用来支持新的特定于JDK8的类型,例如Optional
2、替换默认的ObjectMapper
@Configuration public class LocalDateTimeConfig { public static final String DEFAULT_DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss"; @Bean public ObjectMapper objectMapper() { ObjectMapper objectMapper = new ObjectMapper(); objectMapper.registerModule(new Jdk8Module()); objectMapper.setDateFormat(new SimpleDateFormat(DEFAULT_DATE_TIME_FORMAT)); objectMapper.setTimeZone(TimeZone.getTimeZone("GMT+8")); objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); objectMapper.disable(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE); JavaTimeModule javaTimeModule = new JavaTimeModule(); javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT))); javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT))); objectMapper.registerModule(javaTimeModule).registerModule(new ParameterNamesModule()); return objectMapper; }
答案就在
@Configuration(proxyBeanMethods = false) public class FeignClientsConfiguration { @Autowired private ObjectFactory<HttpMessageConverters> messageConverters; @Bean @ConditionalOnMissingBean public Decoder feignDecoder() { return new OptionalDecoder( new ResponseEntityDecoder(new SpringDecoder(this.messageConverters))); } }
而messageConverters默认的转换器是根据HttpMessageConvertersAutoConfiguration而来
@Configuration(proxyBeanMethods = false) @ConditionalOnClass(HttpMessageConverter.class) @Conditional(NotReactiveWebApplicationCondition.class) @AutoConfigureAfter({ GsonAutoConfiguration.class, JacksonAutoConfiguration.class, JsonbAutoConfiguration.class }) @Import({ JacksonHttpMessageConvertersConfiguration.class, GsonHttpMessageConvertersConfiguration.class, JsonbHttpMessageConvertersConfiguration.class }) public class HttpMessageConvertersAutoConfiguration { static final String PREFERRED_MAPPER_PROPERTY = "spring.http.converters.preferred-json-mapper"; @Bean @ConditionalOnMissingBean public HttpMessageConverters messageConverters(ObjectProvider<HttpMessageConverter<?>> converters) { return new HttpMessageConverters(converters.orderedStream().collect(Collectors.toList())); }
ObjectProvider具有延迟加载的功能,会根据实际情况加载。springboot的web模块默认会引入Jackson相关包。官网上有这么一段话
这个就说明默认的HttpMessageConverter为MappingJackson2HttpMessageConverter,而MappingJackson2HttpMessageConverter会利用objectMapper来进行序列化和反序列化
上面的的话用代码整理如下
public static final String DEFAULT_DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss"; @Bean public Decoder feignDecoder() { return new ResponseEntityDecoder(new SpringDecoder(messageConverters())); } public ObjectFactory<HttpMessageConverters> messageConverters() { return () -> new HttpMessageConverters(mappingJackson2HttpMessageConverter()); } public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() { return new MappingJackson2HttpMessageConverter(objectMapper()); } public ObjectMapper objectMapper() { ObjectMapper objectMapper = new ObjectMapper(); objectMapper.registerModule(new Jdk8Module()); objectMapper.setDateFormat(new SimpleDateFormat(DEFAULT_DATE_TIME_FORMAT)); objectMapper.setTimeZone(TimeZone.getTimeZone("GMT+8")); objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); objectMapper.disable(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE); JavaTimeModule javaTimeModule = new JavaTimeModule(); javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT))); javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT))); objectMapper.registerModule(javaTimeModule).registerModule(new ParameterNamesModule()); return objectMapper; }
异常信息很重要,源码很重要,官网同样重要