Spring cloud feign傳日期類型參數報錯解決

Date類型參數報錯

在Spring cloud feign接口中傳遞Date類型參數時報錯,報錯信息。場景:
客戶端傳遞一個new Date()的參數,服務端接受的參數和客戶端有時間差。
客戶端打印格式化的new Date():java

2018-05-11 10:23:36

而服務端接收到的參數是:spring

2018-05-12 00:23:36

咱們從Feign啓動的源碼能夠看出,Feign在encode和decode時會用SpringEncoder類來實現:json

@Bean
    @ConditionalOnMissingBean
    public Decoder feignDecoder() {
        return new ResponseEntityDecoder(new SpringDecoder(this.messageConverters));
    }

    @Bean
    @ConditionalOnMissingBean
    public Encoder feignEncoder() {
        return new SpringEncoder(this.messageConverters);
    }

而SpringEncoder的HttpMessageConverters使用的是Jackson默認模板,該模板來自基類WebMvcConfigurationSupport.java:
[image:FDEB2409-A536-465C-9229-B65B23F4EFA9-340-0000031DA8363321/4A0D3ACD-13DE-45C3-B7E3-3C7E0CF03A1D.png]
clipboard.pngapp

protected final List<HttpMessageConverter<?>> getMessageConverters() {
        if (this.messageConverters == null) {
            this.messageConverters = new ArrayList<HttpMessageConverter<?>>();
            configureMessageConverters(this.messageConverters);
            if (this.messageConverters.isEmpty()) {
                addDefaultHttpMessageConverters(this.messageConverters);
            }
            extendMessageConverters(this.messageConverters);
        }
        return this.messageConverters;
    }

而WebMvcConfigurationSupport.java最終使用的是默認的ObjectMapper生成的MappingJackson2HttpMessageConverter。至此能夠看出該問題的產生並非Feign的問題,而是Feign實現中使用的Spring MVC中的Jackson轉換參數問題,默認的TimeZone並非東八區,而是UTC。ide

/**
     * Override the default {@link TimeZone} to use for formatting.
     * Default value used is UTC (NOT local timezone).
     * @since 4.1.5
     */
    public Jackson2ObjectMapperBuilder timeZone(TimeZone timeZone) {
        this.timeZone = timeZone;
        return this;
    }

這個問題,在Spring MVC中能夠在接口或者字段上添加註解來解決,但在Feign中使用GET請求的接口添加註解是不行的。debug發現,Spring MVC在處理Date的時候,調用了sun.reflect.ConstructorAccessor#newInstance(Object[] var1),時間會加14個小時。具體實現沒看到源碼,後續再研究。須要說明的是,加JsonFormat註解對於Feign接口沒生效,但Spring MVC是能夠的。
OK,回到正題。要解決這個問題,最好的辦法是自定義ObjectMapper。即便是加了註解能夠解決問題,也依然推薦使用自定義ObjectMapper,由於大量的接口每一個都添加註解太繁瑣了。ui

@Bean
    @Primary
    public ObjectMapper objectMapper() {
        return Jackson2ObjectMapperBuilder.json()
                .serializationInclusion(JsonInclude.Include.NON_NULL)
                .featuresToDisable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
                .timeZone(TimeZone.getTimeZone("Asia/Shanghai"))
                .build();
    }

這樣註解進去的ObjectMapper就帶了時區。this

LocalDate類型報錯

報錯詳情:spa

Failed to read HTTP message: org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Can not construct instance of java.time.LocalDate: no suitable constructor found, can not deserialize from Object value (missing default constructor or creator, or perhaps need to add/enable type information?); nested exception is com.fasterxml.jackson.databind.JsonMappingException: Can not construct instance of java.time.LocalDate: no suitable constructor found, can not deserialize from Object value (missing default constructor or creator, or perhaps need to add/enable type information?)
at [Source: java.io.PushbackInputStream@3ce2b1e2; line: 1, column: 44] (through reference chain: com.chunrun.user.param.UserParams["localDate"])

這是由於LocalDate沒有提供默認的構造器,而Jackson還不支持Java8的特徵。這時候只須要加上依賴,ObjectMapper加一行代碼便可:debug

<dependency>
      <groupId>com.fasterxml.jackson.datatype</groupId>
      <artifactId>jackson-datatype-jsr310</artifactId>
      <version>2.4.0</version>
    </dependency>
@Bean
    @Primary
    public ObjectMapper objectMapper() {
        return Jackson2ObjectMapperBuilder.json()
                .serializationInclusion(JsonInclude.Include.NON_NULL)
                .featuresToDisable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
                .timeZone(TimeZone.getTimeZone("Asia/Shanghai"))
                .modules(new JSR310Module())
                .build();
    }

以上配置調用方也須要。
以上。code

相關文章
相關標籤/搜索