- 引言
在使用 Spring Cloud Feign 调用服务时,常常会遇到日期类型转换错误的问题。因为在服务之间传递日期类型时,很多时候并不使用标准的时间格式(如 ISO 8601),而是使用自定义的日期格式。本篇文章将介绍如何使用 SpringBoot 的自动配置机制来解决这个问题。
- 问题描述
假设我们有一个服务 A 和一个服务 B,A 通过 Feign 调用 B。在传递请求参数时,A 将日期格式转换为自定义格式:
@RestController
public class ServiceAController {
@Autowired
private ServiceBClient serviceBClient;
@GetMapping("test")
public String test() {
return serviceBClient.test(new Date());
}
}
@FeignClient("service-b")
public interface ServiceBClient {
@GetMapping("test")
String test(@RequestParam("date") String date);
}
我们使用这样的方式来调用服务 B 中的 test() 方法。但是,如果服务 B 是使用默认的 Jackson 序列化方式进行 JSON 传递的,则服务 B 则无法正确地进行日期类型的反序列化。最终导致出现类似以下的错误:
com.fasterxml.jackson.databind.exc.InvalidFormatException: Cannot deserialize value of type `java.time.LocalDate` from String "2021-01-01 00:00:00": Failed to deserialize java.time.LocalDate: (java.time.format.DateTimeParseException) Text '2021-01-01 00:00:00' could not be parsed, unable to obtain LocalDateTime from TemporalAccessor: {MinuteOfHour=0, SecondOfMinute=0, NanoOfSecond=0, MicroOfSecond=0, MilliOfSecond=0, HourOfAmPm=0}, ISO resolved to 2021-01-01 of type java.time.format.Parsed
- 解决方案
为了解决该问题,我们需要对 Feign 的请求参数进行自定义序列化和反序列化。具体实现可以通过 SpringBoot 的自动配置机制来实现。
首先,我们需要创建一个实现了Spring 官方提供的ConditionalOnClass和 ConditionalOnMissingBean注解的自动配置类:
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ ObjectMapper.class, Feign.class })
@EnableConfigurationProperties(FeignDateTimeProperties.class)
public class FeignDateTimeFormatConfiguration {
@Autowired(required = false)
private List<Module> jacksonModules = Collections.emptyList();
@Bean
@ConditionalOnMissingBean
@ConditionalOnProperty(value = "feign.date-format.enabled", havingValue = "true", matchIfMissing = false)
public Encoder feignEncoder(ObjectMapper objectMapper, FeignDateTimeProperties properties) {
SimpleModule simpleModule = new SimpleModule();
simpleModule.addSerializer(Date.class, new DateSerializer(properties.getDateFormat()));
objectMapper.registerModule(simpleModule);
objectMapper.registerModules(jacksonModules);
return new JacksonEncoder(objectMapper);
}
@Bean
@ConditionalOnMissingBean
@ConditionalOnProperty(value = "feign.date-format.enabled", havingValue = "true", matchIfMissing = false)
public Decoder feignDecoder(ObjectMapper objectMapper, FeignDateTimeProperties properties) {
SimpleModule simpleModule = new SimpleModule();
simpleModule.addDeserializer(Date.class, new DateDeserializer(properties.getDateFormat()));
objectMapper.registerModule(simpleModule);
objectMapper.registerModules(jacksonModules);
return new JacksonDecoder(objectMapper);
}
}
通过这个配置类,我们可以使用自定义序列化和反序列化器来处理 Feign 请求中传递的日期类型参数。在实现自定义序列化和反序列化器时,我们需要定义两个类:DateSerializer 和 DateDeserializer。
public class DateSerializer extends JsonSerializer<Date> {
private final SimpleDateFormat formatter;
public DateSerializer(String dateFormatPattern) {
this.formatter = new SimpleDateFormat(dateFormatPattern);
}
@Override
public void serialize(Date value, JsonGenerator gen, SerializerProvider provider)
throws IOException {
gen.writeString(formatter.format(value));
}
}
public class DateDeserializer extends JsonDeserializer<Date> {
private final SimpleDateFormat formatter;
public DateDeserializer(String dateFormatPattern) {
this.formatter = new SimpleDateFormat(dateFormatPattern);
}
@Override
public Date deserialize(JsonParser p, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
try {
return formatter.parse(p.getText());
} catch (ParseException e) {
throw new RuntimeException(e);
}
}
}
这样,我们就可以使用自己定义的序列化和反序列化器处理日期类型参数了。配置文件中需要设置一下自定义的日期格式:
## service-b
spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
feign.date-format.enabled=true
feign.date-format.date-format-pattern=${spring.jackson.date-format}
最后,我们需要在 feign client 中添加上configuration配置
@FeignClient(value = "service-b", configuration = {FeignDateTimeFormatConfiguration.class})
public interface ServiceBClient {
@RequestMapping(method = RequestMethod.GET, value = "/test")
String test(@RequestParam("date") Date date);
}
- 示例说明
请参考示例代码:springcloud-feign-date-conversion-demo
该示例中包含了调用服务 A 和服务 B 的完整业务代码,以及如何使用 SpringBoot 自动配置机制解决服务之间日期类型转换问题的示例代码。
- 总结
本篇文章介绍了如何使用 SpringBoot 的自动配置机制解决 Feign 服务间传递日期类型参数出现的转换错误问题。首先,我们需要创建一个自动配置类,定义自定义的序列化器和反序列化器来处理日期类型参数。其次,需要在配置文件中设置日期格式。最后,我们需要在 Feign Client 中添加 configuration 配置,来使用自定义的序列化器和反序列化器。
注意:该解决方案只适用于使用 JSR-310 或者 Java 8 日期类库之前的版本。如果当前已使用这些日期类库,建议一定要使用标准的时间格式进行传输。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:springcloud feign服务之间调用,date类型转换错误的问题 - Python技术站