SpringBoot中的序列化与反序列化:Redis配置
序列化与反序列化都是以网络传输为应用前提的。
- jdk提供序列化接口,是个标记接口;
- jdk提供序列化方法(输入输出流方法);
- springboot对声明的实体类实现序列化接口;
- springboot提供自定义的序列化接口,也称为消息转换器;
- 有第三方库也提供各种MessageConvertor接口,可在MvcConfig中进行配置。
1. 序列化&反序列化
网络传输过程中,通常以字节流形式传输数据,而java-web亦是如此:
Java序列化是指把Java对象转换为字节序列的过程,而Java反序列化是指把字节序列恢复为Java对象的过程。对于基本数据类型,可以很方便地转为字节数组,而对于对象类型,则需要通过序列化器来做。被序列化的对象需要实现
java.io.Serializable
接口,该接口只是一个标记接口,不用实现任何方法。
JDK提供了Java对象的序列化方式实现对象序列化传输,主要通过输出流java.io.ObjectOutputStream
和对象输入流java.io.ObjectInputStream
来实现:
-
java.io.ObjectOutputStream
:表示对象输出流 , 它的writeObject(Object obj)方法可以对参 数指定的obj对象进行序列化,把得到的字节序列写到一个目标输出流中 -
java.io.ObjectInputStream
:表示对象输入流 ,它的readObject()方法源输入流中读取字节序 列,再把它们反序列化成为一个对象,并将其返回
serialVersionUID
这是一个重要的标识,只有序列化对象的~id和这个id一致时,才能反序列化为原对象,所以一般是自己设定一个固定值,如:public static final Long serialVersionUID = 1L。
SpringBoot中的序列化&反序列化
参考:springboot:序列化与反序列化
基本序列化/反序列化
Json是一种轻量级的文本数据交换格式,在Json字符串中{}用来表示对象,[]用来表示列表,数据以key-value的形式存放:
{
"name":"zhangsan",
"age":"22",
"course":["java","python"]
}
在 Spring Boot中,想要一个接口接收Json格式的数据并返回Json格式的数据,前端将http请求头Accept
设置为“application/json”
,Content-Type
为"application/json"
。
中间件只需要在Controller类中做如下定义:
@RestController
@RequestMapping("/example")
public class ExampleController {
@Aurowired
private SomeService someService;
@RequestMapping("/getSth")
public Result getBaseInfo(@RequestBody HashMap<String,Object> map){
return someService.get(map);
}
}
@ResponseBody
在 Controller 中使用@ResponseBody注解即可返回 Json 格式的数据,而@RestController注解包含了@ResponseBody 注解,所以默认情况下,@RestController即可将返回的数据结构转换成Json格式。@RequestBody
相应地,@RequestBody也将接收到的json数据转为相应类型。
而这些注解之所以可以进行Json与JavaBean之间的相互转换,就是因为HttpMessageConverter发挥着作用。HttpMessageConverter
org.springframework.http.converter.HttpMessageConverter
是一个策略接口,是Http request请求和response响应的转换器。Spring为其提供了多个实现类,在项目启动时也会自动加载部分内置的序列化策略(有优先级)。- 自定义序列化器
可以通过配置第三方反/序列化器来定义化一些需求,如LocalDataTime格式的数据转为String、及其反序列化等。
@Override // 注意这里是继承的方法,亦即本来就有部分序列化器被加载到
protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
// 创建转换器
MappingJackson2HttpMessageConverter converter =
new MappingJackson2HttpMessageConverter();
// 设置自定义对象转换器
converter.setObjectMapper(objectMapper); // 这里的objectMapper就是第三方xx了
// 把转换器放入list中:index设置优先级
converters.add(0, converter);
}
@Component
public class JacksonObjectMapper extends ObjectMapper {
public static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd";
public static final String DEFAULT_DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
public static final String DEFAULT_TIME_FORMAT = "HH:mm:ss";
public JacksonObjectMapper() {
super();
//收到未知属性时不报异常
this.configure(FAIL_ON_UNKNOWN_PROPERTIES, false);
//反序列化时,属性不存在的兼容处理
this.getDeserializationConfig().withoutFeatures(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
SimpleModule simpleModule = new SimpleModule()
.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT)))
.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)))
.addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)))
.addSerializer(BigInteger.class, ToStringSerializer.instance)
.addSerializer(Long.class, ToStringSerializer.instance)
.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT)))
.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)))
.addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)));
//注册功能模块 例如,可以添加自定义序列化器和反序列化器
this.registerModule(simpleModule);
}
}
2. redis的序列化
——这里的序列化和web序列化不是一个东西
Redis缓存的两种方式:自定义、结合SpringCache:分别对应RedisTemplate
和CacheManager
(一般使用RedisCacheManager
实现类)。
下为RedisTemplate
,RedisCacheManager
这里不说了:
redis的数据分为key和value(key, hashkey, value, hashvalue),针对key一般都是String类型的序列化方式,而value则一般使用对象2json
的序列化方式。
spring为redis提供以下几种序列化实现:
- Jdk~:默认序列化方式,一般不用它
- String~:String类型序列化方式,底层就是toString
- GenericJackson2~:对象转json序列化方式,将java对象转json类型字符串
- Jackson2Json~:对象转json序列化方式,可自定义ObjectMapper类型属性定制序列化规则
在配置RedisTemplate对应的Bean时设置以上序列化器即可。
@Configuration
public class RedisConfig extends CachingConfigurerSupport {
@Bean
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
// 这个connectionFactory的Bean是哪里定义的:boot帮忙创建的
RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(connectionFactory);
// key序列化:String~
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
redisTemplate.setKeySerializer(stringRedisSerializer);
redisTemplate.setHashKeySerializer(stringRedisSerializer);
// value序列化:obj2json
GenericJackson2JsonRedisSerializer jsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
redisTemplate.setValueSerializer(jsonRedisSerializer);
redisTemplate.setHashValueSerializer(jsonRedisSerializer);
// return Bean
return redisTemplate;
}
}
3. LocalDataTime类型序列化规则
当对象中有时间信息,即LocalDataTime
类型数据时,redis的序列化器会将这个数据拆开(年、月、日等),之后反序列化取对象时就会出错,所以设置其对应的序列化规则(也可以手动转换,即手动转localDT为String,用的时候再转回来),这里应该使用Jackson2Json~
:
@Configuration
public class RedisConfig extends CachingConfigurerSupport {
@Bean
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
// 这个connectionFactory的Bean是哪里定义的:boot帮忙创建的
RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(connectionFactory);
// 默认的Key/Field序列化器为:JdkSerializationRedisSerializer
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
redisTemplate.setKeySerializer(stringRedisSerializer);
redisTemplate.setHashKeySerializer(stringRedisSerializer);
// 使用Jackson2json而非GenericJackson2json:主要为解决LocalDataTime序列化问题
Jackson2JsonRedisSerializer jsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
// 配置localDataTime序列化
jsonRedisSerializer.setObjectMapper(getLocalDataTimeMapper());
// 配置value序列化
redisTemplate.setValueSerializer(jsonRedisSerializer);
redisTemplate.setHashValueSerializer(jsonRedisSerializer);
return redisTemplate;
}
private ObjectMapper getLocalDataTimeMapper() {
// 这里定义了OM中对于LocalDataTime的序列化规则
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
//LocalDatetime序列化
JavaTimeModule timeModule = new JavaTimeModule();
timeModule.addDeserializer(LocalDate.class,
new LocalDateDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
timeModule.addSerializer(LocalDate.class,
new LocalDateSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
timeModule.addDeserializer(LocalDateTime.class,
new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
timeModule.addSerializer(LocalDateTime.class,
new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
// 注册localDataTime序列化
objectMapper.registerModule(timeModule);
return objectMapper;
}
}