问题
在使用RedisTemplate存储对象时,如果采用JDK默认的序列化方式,数据会出现许多编码字符,辨析度不高。比如一个空的User对象,存储到redis后如下:
这些使用JDK默认序列化方式序列化后的数据简直惨不忍睹,在使用命令行查询数据时会很头疼。如何使数据更容易辨别呢?一种办法是使用StringRedisTemplate,在存入redis前先将数据处理成字符串格式再存入redis,但这种方式的缺点就是每次存入数据前都要手动对非字符串数据进行处理。另一种方法就是自定义序列化方式,只需要使用RedisTemplate就能按照自定义的序列化方式存储对象。这里使用的是第二种方法。
环境
这里使用的SpringBoot2.0.5版本。
依赖信息:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.5.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>2.9.9</version>
</dependency>
</dependencies>
SpringBoot启动类:
@SpringBootApplication
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class);
}
}
User实体类:
public class User implements Serializable {
private String username;
private String password;
private DateTime birthday;
public DateTime getBirthday() {
return birthday;
}
public void setBirthday(DateTime birthday) {
this.birthday = birthday;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "User{" +
"username='" + username + '\'' +
", password='" + password + '\'' +
", birthday=" + birthday +
'}';
}
}
测试类:
@RunWith(SpringRunner.class)
@SpringBootTest
public class RedisTest {
@Autowired
private RedisTemplate redisTemplate;
@Test
public void testRedis(){
User user = new User();
user.setUsername("charviki");
user.setPassword("123456");
redisTemplate.opsForValue().set("user", user);
User user1 = (User) redisTemplate.opsForValue().get("user");
System.out.println(user1);
}
}
入口点
当引入redis启动器时,SpringBoot通过RedisTemplate这个类自动帮我们配置了许多默认参数,包括redis主机,默认序列化方式等。找到RedisAutoConfiguration这个类,这个类中有如下代码:
这里使用了注解@ConditionalOnMissingBean(name = "redisTemplate")
,大致意思就是如果Spring容器中没有RedisTemplate这个bean,就会返回一个默认的RedisTemplate(配置信息都在这个类里面)。到这里就有大致的思路了,要想实现自定义redis序列化,首先定义一个返回类型为RedisTemplate的bean,并将该bean交由Spring容器管理。
实现自定义序列化
定义RedisConfig类,自定义序列化:
@Component
public class RedisConfig {
@Bean
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory){
RedisTemplate redisTemplate = new RedisTemplate();
redisTemplate.setConnectionFactory(redisConnectionFactory);
// 自定义key序列化方式,直接将String字符串直接作为redis中的key
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
redisTemplate.setKeySerializer(stringRedisSerializer);
// 自定义value序列化方式,序列化成json格式
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
return redisTemplate;
}
}
运行测试类,程序报错,错误信息如下:
java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to cn.charviki.pojo.User
at cn.charviki.test.RedisTest.testRedis(RedisTest.java:33)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners