springboot序列化
# 页面数据json返回
springboot开发web应用时,响应的json数据,默认采用的是jackson, 它在序列化java8时间时会出错,long序列化时,会丢失精度, hibernate代理对象为空时,也会报错等等,需要进行填坑。
/**
* 参考: org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration
* @author: shollin
* @date: 2021/6/27/027 18:30
*/
@Configuration(proxyBeanMethods=false)
@ConditionalOnClass(ObjectMapper.class)
@AutoConfigureBefore(JacksonAutoConfiguration.class)
public class JacksonConfig {
/**
* 参考 SpringBlade-Tool
* 修复 hibernateLazyInitializer序列化值有空的问题、 jdk8时间日期序列化问题、long序列化丢失精度问题
* @return
*/
@Primary
@Bean
public ObjectMapper objectMapper(Jackson2ObjectMapperBuilder builder) {
SimpleModule simpleModule = new SimpleModule();
/** 序列化配置,针对java8 时间 **/
simpleModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
simpleModule.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
simpleModule.addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern("HH:mm:ss")));
/** 反序列化配置,针对java8 时间 **/
simpleModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
simpleModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
simpleModule.addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern("HH:mm:ss")));
/** Long转String类型 **/
simpleModule.addSerializer(Long.class, ToStringSerializer.instance);
simpleModule.addSerializer(Long.TYPE, ToStringSerializer.instance);
builder.simpleDateFormat(DatePattern.NORM_DATETIME_PATTERN);
//创建ObjectMapper
ObjectMapper objectMapper = builder.createXmlMapper(false).build();
//设置地点为中国
objectMapper.setLocale(Locale.CHINA);
//去掉默认的时间戳格式
objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
//设置为中国上海时区
objectMapper.setTimeZone(TimeZone.getTimeZone(ZoneId.systemDefault()));
//序列化时,日期的统一格式
objectMapper.setDateFormat(new SimpleDateFormat(DatePattern.NORM_DATETIME_PATTERN, Locale.CHINA));
//序列化处理
objectMapper.configure(JsonReadFeature.ALLOW_UNESCAPED_CONTROL_CHARS.mappedFeature(), true);
objectMapper.configure(JsonReadFeature.ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER.mappedFeature(), true);
objectMapper.findAndRegisterModules();
//失败处理
objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
//单引号处理
objectMapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);
//反序列化时,属性不存在的兼容处理
objectMapper.getDeserializationConfig().withoutFeatures(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
//日期格式化
objectMapper.registerModule(simpleModule);
objectMapper.findAndRegisterModules();
return objectMapper;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# 缓存数据序列化及反序列化
RedisAutoConfiguration会自动生成 StringRedisTemplate 和RedisTemplate<Object, Object> 放入容器, 这里定义RedisTemplate<String, Object>,一般key为String,将值用
/**
* redis入口配置文件 参考:
* @author zxp
* @date 2021/7/1 16:15
*/
@EnableCaching
@Configuration(proxyBeanMethods = false)
@AutoConfigureBefore(RedisAutoConfiguration.class)
public class ZxpRedisConfig {
/**
* value 值 序列化
* @return RedisSerializer
*/
@Bean
public RedisSerializer<Object> redisSerializer(ObjectProvider<ObjectMapper> objectProvider) {
// jackson findAndRegisterModules,use copy
ObjectMapper objectMapper = objectProvider.getIfAvailable(ObjectMapper::new).copy();
// findAndRegisterModules
objectMapper.findAndRegisterModules();
// class type info to json
GenericJackson2JsonRedisSerializer.registerNullValueSerializer(objectMapper, null);
objectMapper.activateDefaultTyping(objectMapper.getPolymorphicTypeValidator(), ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
return new GenericJackson2JsonRedisSerializer(objectMapper);
}
@Bean(name = "redisTemplate")
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory,
RedisSerializer<Object> redisSerializer) {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
RedisKeySerializer redisKeySerializer = new RedisKeySerializer();
// key 序列化 采用StringRedisSerializer, key必须为String类型,不然会报错
redisTemplate.setKeySerializer(redisKeySerializer);
redisTemplate.setHashKeySerializer(redisKeySerializer);
// value 序列化
redisTemplate.setValueSerializer(redisSerializer);
redisTemplate.setHashValueSerializer(redisSerializer);
redisTemplate.setConnectionFactory(redisConnectionFactory);
return redisTemplate;
}
@Bean
public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofHours(1));
return RedisCacheManager
.builder(RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory))
.cacheDefaults(redisCacheConfiguration).build();
}
@Bean(name = "redisUtil")
@ConditionalOnBean(RedisTemplate.class)
public RedisUtil redisUtils(RedisTemplate<String, Object> redisTemplate) {
return new RedisUtil(redisTemplate);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# Hibernate JPA中的序列化注解
@Transient、@JsonIgnoreProperties、@JsonIgnore、@JsonFormat、@JsonSerialize等注解解释 @jsonignore的作用: 作用是json序列化时将java bean中的一些属性忽略掉,序列化和反序列化都受影响。 只要在Set方法前增加以下注解即可:
@JsonIgnore
public Set xxxs() {
return this.xxxYyyy;
}
2
3
4
# 1、@Transient
@Transient表示该属性并非一个到数据库表的字段的映射,ORM框架将忽略该属性; 如果一个属性并非数据库表的字段映射,就务必将其标示为@Transient,否则ORM框架默认其注解为@Basic;
# 2、@JsonIgnoreProperties
此注解是类注解,作用是json序列化时将java bean中的一些属性忽略掉,序列化和反序列化都受影响。@JsonIgnoreProperties(ignoreUnknown = true)
,将这个注解写在类上之后,就会忽略类中不存在的字段。
这个注解还可以指定要忽略的字段,例如@JsonIgnoreProperties({ “password”, “secretKey” })
# 3、@JsonIgnore
此注解用于属性或者方法上(最好是属性上),作用和上面的@JsonIgnoreProperties一样。@JsonIgnore注解用来忽略某些字段,可以用在变量或者Getter方法上,用在Setter方法时,和变量效果一样。这个注解一般用在我们要忽略的字段上。
# 4、@JsonFormat
此注解用于属性或者方法上(最好是属性上),可以帮我们完成格式转换,可以方便的把Date类型直接转化为我们想要的模式, 例如对于Date类型字段,如果不适用JsonFormat默认在rest返回的是long, 如果我们使用@JsonFormat(timezone = “GMT+8”, pattern = “yyyy-MM-dd HH:mm:ss”),就返回"2018-11-16 22:58:15"
# 5、@JsonSerialize
此注解用于属性或者getter方法上,用于在序列化时嵌入我们自定义的代码,比如序列化一个double时在其后面限制两位小数点。
# 6、@JsonDeserialize
此注解用于属性或者setter方法上,用于在反序列化时可以嵌入我们自定义的代码,类似于上面的@JsonSerialize
# hibernate懒加载和json序列化冲突
因为懒加载这个对象属性只是一个代理对象,如果json直接当作一个存在的属性去序列化就会出现错误,jpa 默认会使用 hibernate,在 jpa 工作过程中,就会创造代理类来继承 Type ,并添加 handler 和 hibernatelazyinitializer 这两个无须 json 化的属性,所以这里需要用JsonIgnoreProperties 把这两个属性忽略掉。 。。。
或者在class上加上
@JsonIgnoreProperties(value={"hibernateLazyInitializer","handler","fieldHandler"})
public class ProductPrice {
}
2
3
4
或者在这个对象的属性上面增加 @JsonIgnore 注解
@JsonIgnore
public Set<User> getUsers() {
return this.users;
}
2
3
4
实际我们要做的目的就是为了在MappingJackson2HttpMessageConverter通过aop转化为json的时候不去理这个属性