文章目录
- @[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
项目目录结构如下:
这几个类可以单独拿出来放到其它项目中使用,加上相关的pom依赖即可,可以很好的解决应用间fegin调用fastJson数据解析时间类型转换bug
3.ES启动器和代码和应用间fegin调用fastJson数据解析时间类型转换bug修复代码
分享如下:
链接:https://pan.baidu.com/s/1PNcb8phCJp4IVPMsM9-WbA
提取码:3bs3