写在最前面:本文是基于spring boot 2.1.2.RELEASE
首先查看spring boot文档中关于redis的讲解,内容只有下面这些
You can inject an auto-configured RedisConnectionFactory, StringRedisTemplate, or vanilla RedisTemplate instance as you would any other Spring Bean. By default, the instance tries to connect to a Redis server at localhost:6379. The following listing shows an example of such a bean:
@Component public class MyBean { private StringRedisTemplate template; @Autowired public MyBean(StringRedisTemplate template) { this.template = template; } // ... }
If you add your own @Bean of any of the auto-configured types, it replaces the default (except in the case of RedisTemplate, when the exclusion is based on the bean name, redisTemplate, not its type). By default, if commons-pool2 is on the classpath, you get a pooled connection factory.
主要就是说 可以直接注入RedisConnectionFactory、StringRedisTemplate或RedisTemplate(springboot自动配置)。那就开始吧
springboot 集成 redis 快速上手版
使用idea创建springboot工程,依赖勾选上redis
根据spring配置项(https://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html),在application.properties中配置上redis的地址
spring.redis.database=0
spring.redis.host=172.19.0.100
spring.redis.port=6379
spring.redis.password=dayun
敲两个实体类(User.java Address.java)的代码,如下:
public class User implements Serializable {
private String name;
private int age;
private Address address;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
}
public class Address implements Serializable {
private String city;
private String street;
private String detail;
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getStreet() {
return street;
}
public void setStreet(String street) {
this.street = street;
}
public String getDetail() {
return detail;
}
public void setDetail(String detail) {
this.detail = detail;
}
}
编写Test类代码,完成redis写入功能,如下
@RunWith(SpringRunner.class)
@SpringBootTest
public class BlogredisApplicationTests {
@Autowired
RedisTemplate redisTemplate;
@Test
public void contextLoads() {
}
@Test
public void testRedis() {
User user = new User();
user.setName("edward");
user.setAge(30);
Address address = new Address();
address.setCity("sjz");
address.setStreet("长安区");
address.setDetail("10-1-1302");
user.setAddress(address);
redisTemplate.opsForValue().set("user", user);
}
}
运行提示成功,这样就完成springboot 集成redis了。不过查看redis中的写入结果,却发现是一堆看不懂的内容,如下:
![7110279-7a8af930f4104ebe.png](https://upload-images.jianshu.io/upload_images/7110279-7a8af930f4104ebe.png)
这主要是因为将pojo对象序列化为了字节数组写入到redis中,具体在json序列化部分介绍
springboot 集成 redis json序列化版
上面得到一堆看不懂的写入结果,但我们想得到可视化的写入结果,所以又开始了序列化的研究,查阅springboot-data-redis文档发现:
In Spring Data, the conversion between the user (custom) types and raw data (and vice-versa) is handled Redis in the
org.springframework.data.redis.serializer
package.
This package contains two types of serializers that, as the name implies, take care of the serialization process:
- Two-way serializers based on
RedisSerializer
.- Element readers and writers that use
RedisElementReader
andRedisElementWriter
.The main difference between these variants is that
RedisSerializer
primarily serializes tobyte[]
while readers and writers useByteBuffer
.Multiple implementations are available (including two that have been already mentioned in this documentation):
JdkSerializationRedisSerializer
, which is used by default forRedisCache
andRedisTemplate
.- the
StringRedisSerializer
.However one can use
OxmSerializer
for Object/XML mapping through Spring OXM support orJackson2JsonRedisSerializer
orGenericJackson2JsonRedisSerializer
for storing data in JSON format.
Do note that the storage format is not limited only to values. It can be used for keys, values, or hashes without any restrictions.
可以看到,RedisTemplate默认使用的是JdkSerializationRedisSerializer,将key和value都序列化为了字节数组,写入到redis中,就出现了上面我们看不懂的结果。使用Jackson2JsonRedisSerializer或者GenericJackson2JsonRedisSerializer可以得到json形式的序列化结果
有了上面的了解之后,下面要做的就是设置redisTemplate的序列化方式为Jackson2JsonRedisSerializer。
不幸的是查阅了springboot-data-redis文档,并设置redisTemplate和Jackson2JsonRedisSerializer的内容。。没有别的办法,只能百度查阅前辈们的使用方式,找到资料https://www.cnblogs.com/ityouknow/p/5748830.html:主要有想法的部分代码如下:
@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport{
@Bean
public KeyGenerator keyGenerator() {
return new KeyGenerator() {
@Override
public Object generate(Object target, Method method, Object... params) {
StringBuilder sb = new StringBuilder();
sb.append(target.getClass().getName());
sb.append(method.getName());
for (Object obj : params) {
sb.append(obj.toString());
}
return sb.toString();
}
};
}
@SuppressWarnings("rawtypes")
@Bean
public CacheManager cacheManager(RedisTemplate redisTemplate) {
RedisCacheManager rcm = new RedisCacheManager(redisTemplate);
//设置缓存过期时间
//rcm.setDefaultExpiration(60);//秒
return rcm;
}
@Bean
public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory factory) {
StringRedisTemplate template = new StringRedisTemplate(factory);
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
template.setValueSerializer(jackson2JsonRedisSerializer);
template.afterPropertiesSet();
return template;
}
}
虽然使用的springboot版本不太一样,但大的思路应该错不了,自己写个Redis的配置覆盖掉autoconfiguration中的RedisTemplate,这个springboot文档中也提到过,如下:
If you add your own @Bean of any of the auto-configured types, it replaces the default (except in the case of RedisTemplate, when the exclusion is based on the bean name, redisTemplate, not its type). By default, if commons-pool2 is on the classpath, you get a pooled connection factory.
好了,有了思路之后开干,写个自己的redis配置类:
@Configuration
public class RedisConfig {
//还是使用springboot默认配置的RedisConnectionFactory
@Autowired
private RedisConnectionFactory redisConnectionFactory;
// 默认用的是用JdkSerializationRedisSerializer进行序列化的
@Bean
@SuppressWarnings({ "rawtypes", "unchecked" })
public RedisTemplate<String, Object> redisTemplate() {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<String, Object>();
// 注入数据源
redisTemplate.setConnectionFactory(redisConnectionFactory);
// 使用Jackson2JsonRedisSerialize 替换默认序列化
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
// key-value结构序列化数据结构
redisTemplate.setKeySerializer(stringRedisSerializer);
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
// hash数据结构序列化方式,必须这样否则存hash 就是基于jdk序列化的
redisTemplate.setHashKeySerializer(stringRedisSerializer);
redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
// 启用默认序列化方式
redisTemplate.setEnableDefaultSerializer(true);
redisTemplate.setDefaultSerializer(jackson2JsonRedisSerializer);
/// redisTemplate.afterPropertiesSet();
return redisTemplate;
}
}
配置完成之后,再次运行快速集成版中的测试用例,报错
Error:(25, 67) java: 无法访问com.fasterxml.jackson.databind.JavaType
看到错误后,推测肯定缺少jar包,百度了一下将Jackson Databind添加到pom中,再次运行成功写入,结果如下:
![7110279-6ac697583c1a7cdd.png](https://upload-images.jianshu.io/upload_images/7110279-6ac697583c1a7cdd.png)
手动加入Jackson Databind,后来查阅springboot文档,发现:
Auto-configuration for Jackson is provided and Jackson is part of
spring-boot-starter-json
. When Jackson is on the classpath anObjectMapper
bean is automatically configured. Several configuration properties are provided for customizing the configuration of theObjectMapper
.
原来添加spring-boot-starter-json这个依赖就行,这样springboot会自动配置一个ObjectMapper。最终pom文件内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.lqh</groupId>
<artifactId>blogredis</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>blogredis</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-json</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
springboot集成redis Hash处理版
由于我们系统中涉及到redis hash类型,所以要研究一下springboot如何写入redis hash类型。上面查阅springboot-data-redis文档的时候已经看到redis hash的内容,如下:
Data can be stored by using various data structures within Redis.
Jackson2JsonRedisSerializer
can convert objects in JSONformat. Ideally, JSON can be stored as a value by using plain keys. You can achieve a more sophisticated mapping of structured objects by using Redis hashes. Spring Data Redis offers various strategies for mapping data to hashes (depending on the use case):
Direct mapping, by using
HashOperations
and a serializerUsing Redis Repositories
Using
HashMapper
andHashOperations
HashMapper有如下:
Hash mappers are converters of map objects to a
Map<K, V>
and back.HashMapper
is intended for using with Redis Hashes.Multiple implementations are available:
BeanUtilsHashMapper
using Spring’s BeanUtils.
ObjectHashMapper
using Object-to-Hash Mapping.
那就使用HashMapper来处理,有了上面两版的基础之后,这个就很容了,代码如下:
@RunWith(SpringRunner.class)
@SpringBootTest
public class BlogredisApplicationTests {
@Autowired
RedisTemplate redisTemplate;
@Autowired
ObjectMapper objectMapper;
@Test
public void contextLoads() {
}
@Test
public void testRedisWrite() {
User user = new User();
user.setName("edward");
user.setAge(30);
Address address = new Address();
address.setCity("sjz");
address.setStreet("长安区");
address.setDetail("10-1-1302");
user.setAddress(address);
HashMapper<Object, String, Object> mapper = new Jackson2HashMapper(objectMapper, false);
redisTemplate.opsForHash().putAll("user_hash", mapper.toHash(user));
}
@Test
public void testRedisRead() {
Map map = redisTemplate.opsForHash().entries("user_hash");
User user = objectMapper.convertValue(map, User.class);
Assert.assertEquals(user.getAge(), 30);
}
}
主要有一点提示就是:redisTemplate.opsForHash().entries()得到的是map,map转为pojo对象纠结了老半天,后来google发现jackson-bind的objectMapper提供了map转pojo的方法,这下直接用就可以了。
好了,写到这,系统涉及到的redis功能就预研完成了。