1. 配置Redis
1.1. 引入Redis依赖包
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-redis</artifactId>
</dependency>
1.2. 自动装配原理
在项目包libraries下面找到spring-boot-autoconfigure包;在这个包下面有一个spring.factories文件,此文件存放了所有springboot自动装配类的配置信息,以下是redis相关的配置
# mongo的自动装配相关类
org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoReactiveDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoReactiveRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoRepositoriesAutoConfiguration,\
# redis的自动装配相关类
org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration,\
org.springframework.boot.autoconfigure.data.redis.RedisReactiveAutoConfiguration,\
org.springframework.boot.autoconfigure.data.redis.RedisRepositoriesAutoConfiguration,\
springboot所有的配置类都会有一个自动配置类,redis是RedisAutoConfiguration
自动配置类都会绑定一个properties,redis是RedisProperties
RedisAutoConfiguration源码如下:
- 引入了lettuce和jedis的连接配置类,springboot2.X以上版本redis连接都是用的lettuce作为连接客户端,2.X版本之下用的是jedis客户端!
- lettuce客户端:底层连接是基于netty实现的,实例可以在多线程之间实现共享!其调用方法是异步的,lettuce的api接口是线程安全的,方法调用主要采用NIO方式!
- jedis客户端:采用直连的连接方式,使用的阻塞IO,即BIO,性能低,多线程环境下是不安全的,一般可以使用jedispool连接池解决!
@Configuration
@ConditionalOnClass(RedisOperations.class)
@EnableConfigurationProperties(RedisProperties.class)
@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
public class RedisAutoConfiguration {
@Bean
@ConditionalOnMissingBean(name = "redisTemplate")
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory)
throws UnknownHostException {
//默认的redistemplate是没有过多配置的,redis对象都是需要序列化的
//两个泛型都是object,我们后续使用最好是使用RedisTemplate<String, Object>
RedisTemplate<Object, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
@Bean
@ConditionalOnMissingBean
public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory)
throws UnknownHostException {
//string类型是redis中的常见类型,故单独为其提供的操作客户端
StringRedisTemplate template = new StringRedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
}
RedisProperties配置项:
@ConfigurationProperties(prefix = "spring.redis")
public class RedisProperties {
/**
* 默认连接0号数据库
*/
private int database = 0;
/**
* Connection URL:包括 host, port, and password.
* redis://user:password@example.com:6379
*/
private String url;
/**
* Redis server host. 默认是本地地址
*/
private String host = "localhost";
/**
* Login password of the redis server.
*/
private String password;
/**
* Redis server port. 默认6379
*/
private int port = 6379;
/**
* Whether to enable SSL support.
*/
private boolean ssl;
/**
* Connection timeout.
*/
private Duration timeout;
/**
* 哨兵模式配置
*/
private Sentinel sentinel;
/**
* 集群配置
*/
private Cluster cluster;
1.3. 配置文件
spring:
redis:
host: localhost
port: 6379
password: 123456
2.RedisTemplate
2.1. 基本APi
五大基本数据类型和三大特殊类型
redisTemplate.opsForValue(); //操作string类型
redisTemplate.opsForHash(); //操作hash
redisTemplate.opsForList(); //操作list
redisTemplate.opsForSet(); //操作set
redisTemplate.opsForZSet(); //操作zset
redisTemplate.opsForGeo(); //操作geo
redisTemplate.opsForHyperLogLog(); //操作HyperLogLog
事务
redisTemplate.multi(); //开启事务
redisTemplate.exec(); //执行事务
redisTemplate.discard(); //取消事务
redisTemplate.watch(null); //监控数据
redisTemplate.unwatch(); //取消监控
连接
RedisConnectionFactory connectionFactory = redisTemplate.getConnectionFactory();
RedisConnection connection = connectionFactory.getConnection();
connection.flushDb(); //清空数据库
connection.flushAll(); //清空所有数据
简单使用RedisTemplate
package com.acx;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.test.context.junit4.SpringRunner;
@SpringBootTest
@RunWith(SpringRunner.class)
public class RedisTemplateTest {
private static final Logger logger = LoggerFactory.getLogger(RedisTemplateTest.class);
@Autowired
private RedisTemplate redisTemplate;
@Test
public void templateUse(){
redisTemplate.opsForValue().set("user1","zhangsan");
redisTemplate.opsForValue().set("user2","李四");
System.out.println(redisTemplate.opsForValue().get("user1"));
System.out.println(redisTemplate.opsForValue().get("user2"));
}
}
执行结果(控制台):
zhangsan
李四
执行结果(redis服务器):key和value都会出现前缀占位符
本机redis_01:0>keys *
1) "\xAC\xED\x00\x05t\x00\x05user1"
2) "\xAC\xED\x00\x05t\x00\x05user2"
本机redis_01:0>get \xAC\xED\x00\x05t\x00\x05user1
"\xAC\xED\x00\x05t\x00\x08zhangsan"
本机redis_01:0>get \xAC\xED\x00\x05t\x00\x05user2
"\xAC\xED\x00\x05t\x00\x06\xE6\x9D\x8E\xE5\x9B\x9B"
查看RedisTemplate源码可知:
RedisTemplate默认使用的是JdkSerializationRedisSerializer序列化转换器,所以服务器端的key和value都会出现前缀占位符。
if (defaultSerializer == null) {
defaultSerializer = new JdkSerializationRedisSerializer(
classLoader != null ? classLoader : this.getClass().getClassLoader());
}
if (enableDefaultSerializer) {
if (keySerializer == null) {
keySerializer = defaultSerializer;
defaultUsed = true;
}
if (valueSerializer == null) {
valueSerializer = defaultSerializer;
defaultUsed = true;
}
if (hashKeySerializer == null) {
hashKeySerializer = defaultSerializer;
defaultUsed = true;
}
if (hashValueSerializer == null) {
hashValueSerializer = defaultSerializer;
defaultUsed = true;
}
}
2.2. 自定义RedisTemplate
解决问题思路:
- key要使用string序列化器
- value使用jackson或者fastjson等主流的序列化器
- 被序列化对象需要实现Serializable接口
- 使用RedisTemplate<String, Object>
2.2. 自定义RedisTemplate
package com.acx.config;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import java.net.UnknownHostException;
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory)
throws UnknownHostException {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
Jackson2JsonRedisSerializer jacksonSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper om = new ObjectMapper();
//配置json序列化的规则
// 指定要序列化的域,ALL代表包括类的field,get和set,以及修饰符范围,ANY是包括private和public
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
// 指定序列化输入的类型,类必须是非final修饰的,final修饰的类,比如String,Integer等会跑出异常
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jacksonSerializer.setObjectMapper(om);
//字符串序列化类
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
//配置key value序列化规则
template.setKeySerializer(stringRedisSerializer);
template.setValueSerializer(jacksonSerializer);
//配置hash的key value序列化规则
template.setHashKeySerializer(stringRedisSerializer);
template.setHashValueSerializer(jacksonSerializer);
//将RedisTemplate属性进行重新配置
template.afterPropertiesSet();
return template;
}
}
测试代码
- user类:
package com.acx.pojo;
import java.io.Serializable;
public class User implements Serializable {
private String name;
private int age;
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;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
- 测试类:
@Test
public void testUser() {
User user = new User();
user.setName("张三");
user.setAge(34);
redisTemplate.opsForValue().set("user", user);
System.out.println((User) redisTemplate.opsForValue().get("user"));
}
测试结果(控制台):
User{name='张三', age=34}
测试结果(redis服务器):我们发现存在服务器里面的值带上了类名地址,如果微服务中其它的项目没有使用Jackson2JsonRedisSerializer序列化器的话则会导致数据无法解析,故保险起见最好还是将对象转换为json字符串以后再存入redis库,即不配置Jackson2JsonRedisSerializer序列化器。具体使用请根据具体业务实现
本机redis_01:0>keys *
1) "user"
本机redis_01:0>get user
"["com.acx.pojo.User",{"name":"张三","age":34}]"
本机redis_01:0>