背景
- 問題1:專案中使用預設自帶的jackson進行前後端互動,實現資料物件的序列化和反序列化,預設的ObjectMapper採用小駝峰的格式,但是呼叫其他業務的http介面時,ObjectMapper需要使用蛇形的格式,因此就需要自定義ObjectMapper,然後封裝RestTemplate。
- 問題2:前後端互動時,JSR310日期序列化時,格式錯誤;
注意點
- 自定義的ObjectMapper不能被IOC管理,因為Springboot預設的ObjectMapper生成條件是:只有當該例項不存在的時候才會建立!
- 自定義時,需要支援JSR310的日期,否則序列化LocalDateTime、LocalDate、LocalTime時,就會返回錯誤的格式。
- 使用springboot自帶的jackson,是不支援JSR310的日期,可以通過下面方式解決
- 全域性配置
- 在實體類上增加 @JsonFormat(pattern = "yyyy-MM-dd")
- 在controller方法引數中增加 @DateTimeFormat(pattern = "yyyy-MM-dd")
自定義ObjectMapper
@Slf4j
public class JsonFormatUtils {
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");
private static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern("HH:mm:ss");
public static final ObjectMapper objectMapper = new ObjectMapper();
static {
//取消時間的轉化格式,預設是時間戳,同時需要設定要表現的時間格式
objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
objectMapper.configure(SerializationFeature.WRITE_DURATIONS_AS_TIMESTAMPS, false);
objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
JavaTimeModule javaTimeModule = new JavaTimeModule(); // 預設序列化沒有實現,反序列化有實現
javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DATE_TIME_FORMATTER));
javaTimeModule.addSerializer(LocalDate.class, new LocalDateSerializer(DATE_FORMATTER));
javaTimeModule.addSerializer(LocalTime.class, new LocalTimeSerializer(TIME_FORMATTER));
objectMapper.registerModule(javaTimeModule);
// 設定時區
objectMapper.setTimeZone(TimeZone.getDefault());
// 設定格式化輸出
// objectMapper.enable(SerializationFeature.INDENT_OUTPUT);
// 設定蛇形格式
objectMapper.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE);
}
public static String writeValueAsString(Object obj) {
String result = null;
try {
result = objectMapper.writeValueAsString(obj);
} catch (JsonProcessingException e) {
log.error("objectMapper writeValueAsString exception, e:", e);
}
return result;
}
public static <T> T readValue(String str, Class<T> tClass) {
T t = null;
try {
t = objectMapper.readValue(str, tClass);
} catch (JsonProcessingException e) {
log.error("objectMapper readValue exception, e:", e);
}
return t;
}
}
封裝RestTemplate
@Configuration
public class RestTemplateConfig {
@Bean("otherRestTemplate")
public RestTemplate algoRestTemplate() {
// 使用apache的httpComponents,封裝restTemplate
ClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(httpClient());
RestTemplate restTemplate = new RestTemplate(factory);
// 使用自定義的ObjectMapper
ObjectMapper objectMapper = JsonFormatUtils.objectMapper;
MappingJackson2HttpMessageConverter messageConverter = new MappingJackson2HttpMessageConverter();
messageConverter.setPrettyPrint(false);
messageConverter.setObjectMapper(objectMapper);
List<HttpMessageConverter<?>> messageConverters = restTemplate.getMessageConverters();
messageConverters.removeIf(item -> item.getClass().getName().equals(MappingJackson2HttpMessageConverter.class.getName()));
messageConverters.add(messageConverter);
return restTemplate;
}
private HttpClient httpClient() {
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager();
connectionManager.setMaxTotal(50);
connectionManager.setDefaultMaxPerRoute(10);
connectionManager.setValidateAfterInactivity(2000);
RequestConfig requestConfig = RequestConfig.custom()
.setSocketTimeout(10000)
.setConnectTimeout(5000)
.setConnectionRequestTimeout(1000)
.build();
return HttpClientBuilder.create().setDefaultRequestConfig(requestConfig).setConnectionManager(connectionManager).build();
}
}
全域性配置
底層預設是通過Jackson2ObjectMapperBuilderCustomizer建立的ObjectMapper
@Configuration
public class LocalDateTimeSerializerConfig {
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");
private static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern("HH:mm:ss");
@Bean
public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() {
Map<Class<?>, JsonSerializer<?>> serializers = new HashMap<>();
serializers.put(LocalDateTime.class, new LocalDateTimeSerializer(DATE_TIME_FORMATTER));
serializers.put(LocalDate.class, new LocalDateSerializer(DATE_FORMATTER));
serializers.put(LocalTime.class, new LocalTimeSerializer(TIME_FORMATTER));
Map<Class<?>, JsonDeserializer<?>> deserializers = new HashMap<>();
deserializers.put(LocalDateTime.class, new LocalDateTimeDeserializer(DATE_TIME_FORMATTER));
deserializers.put(LocalDate.class, new LocalDateDeserializer(DATE_FORMATTER));
deserializers.put(LocalTime.class, new LocalTimeDeserializer(TIME_FORMATTER));
return builder -> builder.serializersByType(serializers).deserializersByType(deserializers);
}
}