文章目录

  • @[toc]
  • 1.ES启动器实现
  • 1.1 EsAutoConfigure自动装配类
  • 1.2 在resources下新建一个META-INFO文件夹下新增一个spring.factories文件
  • 2.应用间fegin调用fastJson数据解析时间类型转换bug修复
  • 2.1 FastJsonConfig
  • 2.2 JacksonConfig
  • 2.3 LocalDateTimeFormatConfig
  • 2.4 FeignClientConfig
  • 2.5 WebMvcAndJackson2OrFastJsonConfig
  • 3.ES启动器和代码和应用间fegin调用fastJson数据解析时间类型转换bug修复代码

1.ES启动器实现

1.1 EsAutoConfigure自动装配类

package com.zlf.es.spring.boot.autoconfigure;

import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpHost;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestClientBuilder;
import org.elasticsearch.client.RestHighLevelClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.ArrayList;
import java.util.List;

/**
 * @author zlf
 * @description:
 * @time: 2022/06/24
 */
@Configuration
@RefreshScope
public class EsAutoConfigure {

    /**
     * 创建单例模式的RequestOptions,使得所有请求共用
     */
    public static final RequestOptions COMMON_OPTIONS;

    static {
        RequestOptions.Builder builder = RequestOptions.DEFAULT.toBuilder();
        COMMON_OPTIONS = builder.build();
    }

    private final String NPE = "null";

    /**
     * 协议
     */
    @Value("${elasticsearch.schema:http}")
    private String schema;

    /**
     * 集群地址,如果有多个用“,”隔开
     */
    @Value("${elasticsearch.address}")
    private String address;

    /**
     * 账户
     */
    @Value("${elasticsearch.username:null}")
    private String username;

    /**
     * 密码
     */
    @Value("${elasticsearch.password:null}")
    private String password;

    /**
     * 连接超时时间
     */
    @Value("${elasticsearch.connectTimeout:5000}")
    private int connectTimeout;

    /**
     * Socket 连接超时时间
     */
    @Value("${elasticsearch.socketTimeout:30000}")
    private int socketTimeout;

    /**
     * 获取连接的超时时间
     */
    @Value("${elasticsearch.connectionRequestTimeout:5000}")
    private int connectionRequestTimeout;

    /**
     * 最大连接数
     */
    @Value("${elasticsearch.maxConnectNum:100}")
    private int maxConnectNum;

    /**
     * 最大路由连接数
     */
    @Value("${elasticsearch.maxConnectPerRoute:100}")
    private int maxConnectPerRoute;

    @Bean("esClient")
    @ConditionalOnClass(value = {RequestOptions.class, RestHighLevelClient.class})
    public RestHighLevelClient restHighLevelClient() {
        // 拆分地址
        List<HttpHost> hostLists = new ArrayList<>();
        if (address.equals(NPE)) {
            throw new RuntimeException("es的address列表没有配置,请检查配置");
        }
        String[] hostList = address.split(",");
        if (hostList.length == 1) {
            String host = hostList[0].split(":")[0];
            String port = hostList[0].split(":")[1];
            hostLists.add(new HttpHost(host, Integer.parseInt(port), schema));
        } else {
            for (String addr : hostList) {
                String host = addr.split(":")[0];
                String port = addr.split(":")[1];
                hostLists.add(new HttpHost(host, Integer.parseInt(port), schema));
            }
        }
        // 转换成 HttpHost 数组
        HttpHost[] httpHost = hostLists.toArray(new HttpHost[]{});
        // 构建连接对象
        RestClientBuilder builder = RestClient.builder(httpHost);
        if (StringUtils.isNotEmpty(username) && !NPE.equals(username) && StringUtils.isNotEmpty(password) && !NPE.equals(password)) {
            //设置用户名和密码
            final CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
            credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(username, password));

            builder.setHttpClientConfigCallback(httpClientBuilder -> {
                httpClientBuilder.disableAuthCaching();
                return httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider);
            });
        }
        // 异步连接延时配置
        builder.setRequestConfigCallback(requestConfigBuilder -> {
            requestConfigBuilder.setConnectTimeout(connectTimeout);
            requestConfigBuilder.setSocketTimeout(socketTimeout);
            requestConfigBuilder.setConnectionRequestTimeout(connectionRequestTimeout);
            return requestConfigBuilder;
        });
        // 异步连接数配置
        builder.setHttpClientConfigCallback(httpClientBuilder -> {
            httpClientBuilder.setMaxConnTotal(maxConnectNum);
            httpClientBuilder.setMaxConnPerRoute(maxConnectPerRoute);
            return httpClientBuilder;
        });
        return new RestHighLevelClient(builder);
    }

}

1.2 在resources下新建一个META-INFO文件夹下新增一个spring.factories文件

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  com.zlf.es.spring.boot.autoconfigure.EsAutoConfigure,\
  com.zlf.es.spring.boot.autoconfigure.service.impl.DocServiceImpl,\
  com.zlf.es.spring.boot.autoconfigure.service.impl.IndexServiceImpl

    该文件就是Start启动器的核心配置文件,Start启动的原理就不做过多的讲解,本文针对如何封装一个Start的代码示例,EsAutoConfigure这个是ES自动装配的类,IndexServiceImpl是我分装的一套关于操作index索引的API,DocServiceImpl是我封装的一套关于文档操作的API.

1.3 使用方法

只需要在nacos的yml中配置:

elasticsearch:
 address: ip:9200
 username: 
 password:

然后在项目中引入下依赖:

<properties>
       <es.rest.high.level.client>7.14.2</es.rest.high.level.client>
 </properties> 
  <dependency>
       <groupId>org.zlf</groupId>
       <artifactId>es-spring-boot-start</artifactId>
       <version>1.0-SNAPSHOT</version>
  </dependency>
  <dependency>
       <groupId>org.elasticsearch.client</groupId>
       <artifactId>elasticsearch-rest-high-level-client</artifactId>
       <version>${es.rest.high.level.client}</version>
  </dependency>
  <dependency>
        <groupId>org.elasticsearch</groupId>
        <artifactId>elasticsearch</artifactId>
        <version>${es.rest.high.level.client}</version>
  </dependency>
  <dependency>
         <groupId>org.elasticsearch.client</groupId>
         <artifactId>elasticsearch-rest-client</artifactId>
         <version>${es.rest.high.level.client}</version>
  </dependency>

即可轻松使用es,该pom中的ES的版本使用的是7.14.2,后续可以丰富和完成这个Start.


2.应用间fegin调用fastJson数据解析时间类型转换bug修复

    之前做一个项目使用feign来调用SpringCloudAliBaBa的服务,结果调用报了一个莫名其妙的错误

Caused by: com.alibaba.fastjson.JSONException: write javaBean error, fastjson version 1.2.76, class com.xxx.framework.api.response.RestResponse, java.time.LocalDateTime cannot be cast to java.util.Date

    遇到这种莫名其妙的错误也是让人头大,这个问题让我搞了一个星期才完美的解决了,通过去看网上一些教程和自己看源码之后,走过了一些坑,最后给它解决了,我还搞了一个fegin调用HttpMessageConverter支持jackSon和fastJson两种编解码转换器,废话不多说,直接上代码:

项目中需要有fastJson的依赖:

<dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.79</version>
        </dependency>-->

在项目下新建一个包名为com.alibaba.fastjson.serializer下放一个SimpleDateFormatSerializer类:

package com.alibaba.fastjson.serializer;

import java.io.IOException;
import java.lang.reflect.Type;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Date;

/**
 * 1).jackson和fastjson序列化和反序列化混用,
 * 只要json的格式都是一个标准都可以解析的
 * (除非两个库的编码实现上有差异,有特特殊的字符或类型的序列化和反序列化有差异的话,就会不能相互解析
 * <p>
 * 2).fastJson的bug
 * fastJson的JSON.toJSONString(object obj)序列化
 * 只要实体中含有LocalDateTime的字段,在fastJson没有指定SerializeConfig序列化时间的方式时会默认的序列化为java的UTC时间类型。格式为:
 * yyyy-MM-dd'T' HH:mm:ss,如果指定了SerializeConfig序列化LocalDateTime的方式时,fastJson默认支持的时间字段累类型是Date,所以指定
 * LocalDateTime的序列化方式的时候,JSON.toJSONString(object obj)会执行失败,失败报错就是LocalDateTime不能直接转为Date类型,所以我们要重写fastJson的时间格式化解析器的源码
 */

/**
 * @author zlf
 * @description:
 * @time: 2022/06/24
 */
public class SimpleDateFormatSerializer implements ObjectSerializer {

    private final String pattern;

    public SimpleDateFormatSerializer(String pattern) {
        this.pattern = pattern;
    }

    @Override
    public void write(JSONSerializer serializer, Object object, Object fieldName, Type fieldType, int features) throws IOException {
        if (object == null) {
            serializer.out.writeNull();
            return;
        }
        Date date = null;
        if (object instanceof LocalDateTime) {
            LocalDateTime localDateTime = (LocalDateTime) object;
            date = Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
        } else if (object instanceof LocalDate) {
            LocalDate localDate = (LocalDate) object;
            date = Date.from(localDate.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant());
        } else {
            date = (Date) object;
        }
        SimpleDateFormat format = new SimpleDateFormat(pattern, serializer.locale);
        format.setTimeZone(serializer.timeZone);

        String text = format.format(date);
        serializer.write(text);
    }

}

    这个类是用来解决fastJson时间类型转换的序列化解析器,fastJson默认是不支持LocalDateTime、LocalDate类型直接转换为Date类型的,所以这个是使用fastJson的一个大坑,这个算是fastJson的一个bug,本来想给fastJsong官方提一个bug的,后面想想算了,这个坑也是坑的,官方解决不了,那只能自己解决了,自己改它的源码在自己项目中修复这个bug了。

    在config包下新增FastJsonConfig(fastJson配置)、JacksonConfig(jackSon配置)、LocalDateTimeFormatConfig(spring的@DateTimeFormat(pattern = “yyyy-MM-dd HH:mm:ss”)实体注解格式解析配置)、FeignClientConfig(fegin的编解码配置,支持jackSon和fasJson根据需要配置支持,Spring默认的HttpMessageConverter解析的是jackSon解析,不支持fastJson解析,所以需要自己实现)、WebMvcAndJackson2OrFastJsonConfig(配置jackSon和fastJson的转换器加入到springMvc的List中,默认jackSon的顺序是在fastJson前面的)这几个配置类:

2.1 FastJsonConfig

package com.zlf.es.spring.boot.autoconfigure.config;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializeConfig;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.alibaba.fastjson.serializer.SimpleDateFormatSerializer;
import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;

import java.nio.charset.Charset;
import java.sql.Timestamp;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * @author zlf
 * @description:
 * @time: 2022/06/24
 */
@Data
@Configuration
@RefreshScope
public class FastJsonConfig {

    /**
     * 可以配置为:NO,YES,ALL
     */
    @Value("${isUseFastJsonHMC:NO}")
    private String isUseFastJsonHMC;

    /**
     * jackson的消息解析器优先,同时也支持fastJson的消息解析的
     */
    public static final String ALL = "ALL";

    /**
     * fastJson消息转换器
     *
     * @return
     */
    @Bean
    @ConditionalOnClass(JSON.class)
    public FastJsonHttpMessageConverter fastJsonHttpMessageConverter() {
        FastJsonHttpMessageConverter fastJsonHttpMessageConverter = new FastJsonHttpMessageConverter();
        //1.添加fastJson的配置信息;
        com.alibaba.fastjson.support.config.FastJsonConfig fastJsonConfig = new com.alibaba.fastjson.support.config.FastJsonConfig();
        fastJsonConfig.setCharset(Charset.forName("UTF-8"));
        fastJsonConfig.setSerializerFeatures(
                SerializerFeature.PrettyFormat,//PrettyFormat漂亮的json格式
                SerializerFeature.WriteNonStringKeyAsString, //不是String类型的key转换成String类型,否则前台无法将Json字符串转换成Json对象
                SerializerFeature.WriteMapNullValue,        // 是否输出值为null的字段,默认为false,我们将它打开
                SerializerFeature.WriteNullListAsEmpty,     // 将Collection类型字段的字段空值输出为[]
                SerializerFeature.WriteNullStringAsEmpty,   // 将字符串类型字段的空值输出为空字符串
                SerializerFeature.WriteNullNumberAsZero,    // 将数值类型字段的空值输出为0
                SerializerFeature.WriteDateUseDateFormat,
                SerializerFeature.DisableCircularReferenceDetect    // 禁用循环引用
        );
        // 缺点,指定后,将不会使用@JSONField注解上的format属性,包括并不限于Date类,LocalDateTime类,LocalDate类。(慎用)
        //文本date的格式化方式
        fastJsonConfig.setDateFormat("yyyy-MM-dd HH:mm:ss");
        //2.序列化方式配置
        SerializeConfig serializeConfig = fastJsonConfig.getSerializeConfig();
        serializeConfig.put(LocalDateTime.class, new SimpleDateFormatSerializer("yyyy-MM-dd HH:mm:ss"));
        serializeConfig.put(Timestamp.class, new SimpleDateFormatSerializer("yyyy-MM-dd HH:mm:ss"));
        serializeConfig.put(LocalDate.class, new SimpleDateFormatSerializer("yyyy-MM-dd"));
        serializeConfig.put(Date.class, new SimpleDateFormatSerializer("yyyy-MM-dd"));
        //3处理中文乱码问题
        List<MediaType> fastMediaTypes = new ArrayList<>();
        fastMediaTypes.add(MediaType.APPLICATION_JSON);
        fastMediaTypes.add(MediaType.parseMediaType(MediaType.TEXT_PLAIN_VALUE + ";charset=ISO-8859-1"));

        //4.在convert中添加配置信息.
        fastJsonHttpMessageConverter.setSupportedMediaTypes(fastMediaTypes);
        fastJsonHttpMessageConverter.setFastJsonConfig(fastJsonConfig);

        return fastJsonHttpMessageConverter;
    }

}

2.2 JacksonConfig

package com.zlf.es.spring.boot.autoconfigure.config;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.Locale;
import java.util.TimeZone;

/**
 * @author zlf
 * @description:
 * @time: 2022/06/24
 */
@Configuration
public class JacksonConfig {

    /**
     * Date格式化字符串
     */
    private static final String DATE_FORMAT = "yyyy-MM-dd";
    /**
     * DateTime格式化字符串
     */
    private static final String DATETIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
    /**
     * Time格式化字符串
     */
    private static final String TIME_FORMAT = "HH:mm:ss";

    @Bean
    @ConditionalOnClass(ObjectMapper.class)
    public Jackson2ObjectMapperBuilder jackson2ObjectMapperBuilder() {
        return new Jackson2ObjectMapperBuilder();
    }

    /**
     * 自定义了一个Jackson2ObjectMapperBuilder
     *
     * @return
     */
    @Bean
    @ConditionalOnClass(Jackson2ObjectMapperBuilder.class)
    public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() {
        return builder -> {
            builder.serializerByType(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DATETIME_FORMAT)));
            builder.deserializerByType(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(DATETIME_FORMAT)));
            builder.serializerByType(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern(DATE_FORMAT)));
            builder.deserializerByType(LocalDate.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern(DATE_FORMAT)));
            builder.serializerByType(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern(TIME_FORMAT)));
            builder.deserializerByType(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern(TIME_FORMAT)));
            builder.locale(Locale.SIMPLIFIED_CHINESE);
            /**spring:
             *   jackson:
             *     time-zone: Asia/Shanghai
             *     date-format: yyyy-MM-dd HH:mm:ss
             * 一些常用配置可以配置
             */
            builder.simpleDateFormat(DATETIME_FORMAT);
            builder.timeZone(TimeZone.getTimeZone("Asia/Shanghai"));
        };
    }

    /**
     * 1.禁止springBoot的空对象序列化,否则springMvc返回给前端要被序列化为json的实体中含有空对象就会报错
     * 2.忽略为null的字段
     * 3.反序列化实体中缺少字段不报com.fasterxml.jackson.databind.exc.MismatchedInputException错
     * 4.通过Jackson2ObjectMapperBuilder构建一个单例的ObjectMapper
     * 自定义LocalDateTime类型的编解码
     *
     * @return
     */
    @Bean
    @ConditionalOnClass(Jackson2ObjectMapperBuilder.class)
    public ObjectMapper objectMapper(Jackson2ObjectMapperBuilder jackson2ObjectMapperBuilder) {
        //针对于Date类型,文本格式化
        //jackson2ObjectMapperBuilder.simpleDateFormat(PATTERN1);
        //针对于JDK新时间类。序列化时带有T的问题,自定义格式化字符串
        JavaTimeModule javaTimeModule = new JavaTimeModule();
        javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DATETIME_FORMAT)));
        javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(DATETIME_FORMAT)));
        javaTimeModule.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern(DATE_FORMAT)));
        javaTimeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern(DATE_FORMAT)));
        javaTimeModule.addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern(TIME_FORMAT)));
        javaTimeModule.addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern(TIME_FORMAT)));
        ObjectMapper objectMapper = jackson2ObjectMapperBuilder.build();
        objectMapper.registerModule(javaTimeModule);
        objectMapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS).setSerializationInclusion(JsonInclude.Include.NON_NULL);
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        return objectMapper;
    }

    /*
    jackson消息转换器(默认使用这个消息转换器)
     * 注意:
     *  在配置springBoot支付jakson序列化返回给前端的配置中要配置ObjectMapper的序列化方式的话需要如下这种配置:
     *  在有feigin集成调用的时候然后直接配置一个单例的ObjectMapper就会让fegin反序列化时间字段报错,
     *  原因是由于单例的ObjectMapper会让feigin的解码器和springDecode的jackson支持的解码器相互冲突影响。
     *
     * */
    @Bean
    @ConditionalOnClass(ObjectMapper.class)
    public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() {
        MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter = new MappingJackson2HttpMessageConverter(
                this.objectMapper(this.jackson2ObjectMapperBuilder()));
        /**设置mediaType支持可以不用设置.默认是这个设置的
         * public MappingJackson2HttpMessageConverter(ObjectMapper objectMapper) {
         *         super(objectMapper, new MediaType[]{MediaType.APPLICATION_JSON, new MediaType("application", "*+json")});
         *     }
         */
       /* List<MediaType> mediaType = new ArrayList<MediaType>();
        mediaType.add(MediaType.APPLICATION_JSON);
        mappingJackson2HttpMessageConverter.setSupportedMediaTypes(mediaType);*/
        return mappingJackson2HttpMessageConverter;
    }

}

2.3 LocalDateTimeFormatConfig

package com.zlf.es.spring.boot.autoconfigure.config;

import com.sun.istack.Nullable;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.format.Formatter;
import org.springframework.util.StringUtils;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.Locale;
import java.util.Objects;

/**
 * @author zlf
 * @description:
 * @time: 2022/06/24
 * 时间全局处理
 * 可以使用@DateTimeFormat(pattern = "xxxx")标注在指定的字段上
 */
@Configuration
public class LocalDateTimeFormatConfig {

    @Bean
    public Formatter<LocalDate> localDateFormatter() {
        return new Formatter<LocalDate>() {
            @Override
            public @Nullable
            String print(@Nullable LocalDate object, @Nullable Locale locale) {
                if (Objects.isNull(object)) {
                    return null;
                }
                return object.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
            }

            @Override
            public @Nullable
            LocalDate parse(@Nullable String text, @Nullable Locale locale) {
                if (!StringUtils.hasText(text)) {
                    return null;
                }
                return LocalDate.parse(text, DateTimeFormatter.ofPattern("yyyy-MM-dd"));
            }
        };
    }

    @Bean
    public Formatter<LocalDateTime> localDateTimeFormatter() {
        return new Formatter<LocalDateTime>() {
            @Override
            public @Nullable
            String print(@Nullable LocalDateTime object, @Nullable Locale locale) {
                if (Objects.isNull(object)) {
                    return null;
                }
                return object.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
            }

            @Override
            public @Nullable
            LocalDateTime parse(@Nullable String text, @Nullable Locale locale) {
                if (!StringUtils.hasText(text)) {
                    return null;
                }
                return LocalDateTime.parse(text, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
            }
        };
    }

    @Bean
    public Formatter<LocalTime> localTimeFormatter() {
        return new Formatter<LocalTime>() {
            @Override
            public @Nullable
            String print(@Nullable LocalTime object, @Nullable Locale locale) {
                if (Objects.isNull(object)) {
                    return null;
                }
                return object.format(DateTimeFormatter.ofPattern("HH:mm:ss"));
            }

            @Override
            public @Nullable
            LocalTime parse(@Nullable String text, @Nullable Locale locale) {
                if (!StringUtils.hasText(text)) {
                    return null;
                }
                return LocalTime.parse(text, DateTimeFormatter.ofPattern("HH:mm:ss"));
            }
        };
    }

}

2.4 FeignClientConfig

package com.zlf.es.spring.boot.autoconfigure.config;

import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
import feign.codec.Decoder;
import feign.codec.Encoder;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.cloud.openfeign.support.SpringDecoder;
import org.springframework.cloud.openfeign.support.SpringEncoder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;

/**
 * @author zlf
 * @description:
 * @time: 2022/06/24
 * fegin的
 */
@Configuration
public class FeignClientConfig {

   /* @Autowired(required = false)
    private JacksonConfig jacksonConfig;

    @Autowired(required = false)
    private FastJsonConfig fastJsonConfig;*/

    @Bean
    public Encoder feignEncoder() {
        return new SpringEncoder(feignHttpMessageConverter());
    }

    @Bean
    public Decoder feignDecoder() {
        return new SpringDecoder(feignHttpMessageConverter());
    }

    private ObjectFactory<HttpMessageConverters> feignHttpMessageConverter() {
        JacksonConfig jacksonConfig = new JacksonConfig();
        FastJsonConfig fastJsonConfig = new FastJsonConfig();
        Collection<HttpMessageConverter<?>> converters = new ArrayList<>();
        MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter = jacksonConfig.mappingJackson2HttpMessageConverter();
        FastJsonHttpMessageConverter fsc = fastJsonConfig.fastJsonHttpMessageConverter();
        converters.add(mappingJackson2HttpMessageConverter);
        if (Boolean.valueOf(fastJsonConfig.getIsUseFastJsonHMC())) {
            converters.add(fsc);
            Iterator<HttpMessageConverter<?>> iterator = converters.iterator();
            while (iterator.hasNext()) {
                HttpMessageConverter<?> next = iterator.next();
                if (next instanceof MappingJackson2HttpMessageConverter) {
                    iterator.remove();
                }
            }
        } else if (String.valueOf(fastJsonConfig.getIsUseFastJsonHMC()).equals(FastJsonConfig.ALL)) {
            converters.add(fastJsonConfig.fastJsonHttpMessageConverter());
        }
        //设置中文编码格式
        List<MediaType> list = new ArrayList<MediaType>();
        list.add(MediaType.APPLICATION_JSON_UTF8);
        list.add(MediaType.APPLICATION_JSON);
        list.add(MediaType.TEXT_PLAIN);
        list.add(MediaType.ALL);
        mappingJackson2HttpMessageConverter.setSupportedMediaTypes(list);
        final HttpMessageConverters httpMessageConverters = new HttpMessageConverters(converters);
        return () -> httpMessageConverters;
    }

}

2.5 WebMvcAndJackson2OrFastJsonConfig

package com.zlf.es.spring.boot.autoconfigure.config;

import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.util.Iterator;
import java.util.List;

/**
 * @author zlf
 * @description:
 * @time: 2022/06/24
 * 
 * 1.WebMvcConfigurerAdapter springBoot1.x
 * springBoot2.x推荐使用WebMvcConfigurer
 * 2.实现WebMvcConfigurer类不会让WebMvcAutoConfiguration自动装配置失效和静态资源访问失效
 * 3.继承WebMvcConfigurationSupport会覆盖原有的WebMvcConfigurationSupport类,、
 * 还会让WebMvcAutoConfiguration自动装配置失效和静态资源访问失效
 */
@Slf4j
@Configuration
public class WebMvcAndJackson2OrFastJsonConfig implements WebMvcConfigurer {

    /*@Autowired(required = false)
    private JacksonConfig jacksonConfig;

    @Autowired(required = false)
    private FastJsonConfig fastJsonConfig;*/

    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        JacksonConfig jacksonConfig = new JacksonConfig();
        FastJsonConfig fastJsonConfig = new FastJsonConfig();
        /**
         * 此处已经设置了MappingJackson2HttpMessageConverter jackson转换器,但是未设置任何转换格式,
         * 所以当需要反序列化String类型为Timestamp类型时,
         * 用的格式仍然Jackson默认的类型格式-yyyy-MM-dd'T'HH:mm:ss.SSSZ,转
         *  @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
         *  private LocalDateTime timestamp 报错
         * 导致反序列化失败,报错,而且此处设置便会覆盖配置文件的 jackson的配置项,配置一直不生效的原因就是这里
         **/
        converters.add(0, jacksonConfig.mappingJackson2HttpMessageConverter());
        /**
         * 如果使用fastJson消息转换器必须把默认的jackson的消息转换器移除,在把fastJson的转换器加入进入,
         * 还是可以自动匹配?
         */
        if (Boolean.valueOf(fastJsonConfig.getIsUseFastJsonHMC())) {
            Iterator<HttpMessageConverter<?>> iterator = converters.iterator();
            while (iterator.hasNext()) {
                HttpMessageConverter<?> next = iterator.next();
                if (next instanceof MappingJackson2HttpMessageConverter) {
                    iterator.remove();
                }
            }
            converters.add(0, fastJsonConfig.fastJsonHttpMessageConverter());
            log.info("==============配置fastJsonHttpMessageConverter成功!===============");
        } else if (String.valueOf(fastJsonConfig.getIsUseFastJsonHMC()).equals(FastJsonConfig.ALL)) {
            converters.add(1, fastJsonConfig.fastJsonHttpMessageConverter());
        }
    }

}

    在nacos的项目配置yml文件中需要加入如下的配置,开启fegin调用支持jackSon和fastJson的转化解析,且fastJson解析时间类型不会出现异常:

isUseFastJsonHMC: ALL

项目目录结构如下:

es 怎么看出来时时区_elasticsearch

    这几个类可以单独拿出来放到其它项目中使用,加上相关的pom依赖即可,可以很好的解决应用间fegin调用fastJson数据解析时间类型转换bug


3.ES启动器和代码和应用间fegin调用fastJson数据解析时间类型转换bug修复代码

分享如下:

链接:https://pan.baidu.com/s/1PNcb8phCJp4IVPMsM9-WbA 
提取码:3bs3