Spring Boot 2.0 集成 redis

Spring Boot 2.x 开始 Lettuce 已取代 Jedis 成为首选 Redis 的客户端。固然 Spring Boot 2.x 仍然支持 Jedis,而且你能够任意切换客户端。java

Lettuce

Lettuce 是一个可伸缩的线程安全的 Redis 客户端,支持同步、异步和响应式模式。多个线程能够共享一个链接实例,而没必要担忧多线程并发问题。它基于优秀 Netty NIO 框架构建,支持 Redis 的高级功能,如 Sentinel、集群、流水线、自动从新链接和 Redis 数据模型redis

Jedis 在实现上是直接链接的 redis server,若是在多线程环境下是非线程安全的,这个时候只有使用链接池,为每一个 Jedis 实例增长物理链接。spring

Lettuce 的链接是基于 Netty 的,链接实例 (StatefulRedisConnection) 能够在多个线程间并发访问,应为 StatefulRedisConnection 是线程安全的,因此一个链接实例 (StatefulRedisConnection) 就能够知足多线程环境下的并发访问,固然这个也是可伸缩的设计,一个链接实例不够的状况也能够按需增长链接实例。         数据库

Spring Boot 2.0 集成 redis

通常须要4步apache

  1. 引入依赖
  2. 配置 redis
  3. 自定义 RedisTemplate (推荐)
  4. 自定义 redis 操做类 (推荐)

引入依赖

<!--redis-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

<!-- lettuce pool 缓存链接池 -->
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
</dependency>

<!--jackson-->
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
</dependency>
复制代码
  • 若是用的是 lettuce 客户端,须要引入 commons-pool2 链接池。
  • 若是想用 json 序列化 redis 的 value 值,须要引入 jackson

配置 redis

# redis 服务端相关配置
# 服务器地址
spring.redis.host=localhost
# 端口号
spring.redis.port=6379
# 密码,默认为 null
spring.redis.password=
# 使用的数据库,默认选择下标为0的数据库
spring.redis.database=0
# 客户端超时时间,默认是2000ms
spring.redis.timeout=2000ms


## jedis 客户端配置(从 Spring Boot 2.x 开始,再也不推荐使用 jedis 客户端)
## 创建链接最大等待时间,默认1ms,超出该时间会抛异常。设为-1表示无限等待,直到分配成功。
#spring.redis.jedis.pool.max-wait=1ms
## 最大连链接数,默认为8,负值表示没有限制
#spring.redis.jedis.pool.max-active=8
## 最大空闲链接数,默认8。负值表示没有限制
#spring.redis.jedis.pool.max-idle=8
## 最小空闲链接数,默认0。
#spring.redis.jedis.pool.min-idle=0


# lettuce 客户端配置(从 Spring Boot 2.x 开始,推荐使用 lettuce 客户端)
# 创建链接最大等待时间,默认1ms,超出该时间会抛异常。设为-1表示无限等待,直到分配成功。
spring.redis.lettuce.pool.max-wait=1ms
# 最大连链接数,默认为8,负值表示没有限制
spring.redis.lettuce.pool.max-active=8
# 最大空闲链接数,默认8。负值表示没有限制
spring.redis.lettuce.pool.max-idle=8
# 最小空闲链接数,默认0。
spring.redis.lettuce.pool.min-idle=0
# 设置关闭链接的超时时间
spring.redis.lettuce.shutdown-timeout=100ms
复制代码

自定义 RedisTemplate

RedisTemplate 是 spring 为咱们提供的 redis 操做类,经过它咱们能够完成大部分 redis 操做。json

只要咱们引入了 redis 依赖,并将 redis 的链接信息配置正确,springboot 就会根据咱们的配置会给咱们生成默认 RedisTemplate。缓存

可是默认生成的 RedisTemplate 有两个地方不是很符合平常开发中的使用习惯安全

  1. 默认生成的 RedisTemplate<K, V> 接收的keyvalue为泛型,常常须要类型转换,直接使用不是很方便
  2. 默认生成的 RedisTemplate 序列化时,使用的是 JdkSerializationRedisSerializer ,存储到 redis 中后,内容为二进制字节,不利于查看原始内容

对于第一个问题,通常习惯将 RedisTemplate<K, V> 改成 RedisTemplate<String, Object>,即接收的 keyString 类型,接收的 valueObject 类型 对于第二个问题,通常会把数据序列化为 json 格式,而后存储到 redis 中,序列化成 json 格式还有一个好处就是跨语言,其余语言也能够读取你存储在 redis 中的内容springboot

为了实现上面两个目的,咱们须要自定义本身的 RedisTemplatebash

以下,建立一个 config 类,在里面配置 自定义的 RedisTemplate

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
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;

@Configuration
// 控制配置类的加载顺序,先加载 RedisAutoConfiguration.class 再加载该类,这样才能覆盖默认的 RedisTemplate
@AutoConfigureAfter(RedisAutoConfiguration.class)
public class RedisConfig {
    /**
     * 自定义 redisTemplate (方法名必定要叫 redisTemplate 由于 @Bean 是根据方法名配置这个bean的name的)
     * 默认的 RedisTemplate<K,V> 为泛型,使用时不太方便,自定义为 <String, Object>
     * 默认序列化方式为 JdkSerializationRedisSerializer 序列化后的内容不方便阅读,改成序列化成 json
     *
     * @param redisConnectionFactory
     * @return
     */
    @Bean
    RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        // 配置 json 序列化器 - Jackson2JsonRedisSerializer
        Jackson2JsonRedisSerializer jacksonSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jacksonSerializer.setObjectMapper(objectMapper);

        // 建立并配置自定义 RedisTemplateRedisOperator
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(redisConnectionFactory);
        // 将 key 序列化成字符串
        template.setKeySerializer(new StringRedisSerializer());
        // 将 hash 的 key 序列化成字符串
        template.setHashKeySerializer(new StringRedisSerializer());
        // 将 value 序列化成 json
        template.setValueSerializer(jacksonSerializer);
        // 将 hash 的 value 序列化成 json
        template.setHashValueSerializer(jacksonSerializer);
        template.afterPropertiesSet();
        return template;
    }
}
复制代码

自定义 Redis 操做类

虽然 RedisTemplate 已经对 redis 的操做进行了必定程度的封装,可是直接使用仍是有些不方便,实际开发中,通常会对 RedisTemplate 作近一步封装,造成一个简单、方便使用的Redis 操做类。

固然你也能够选择不封装,看我的喜爱。

具体的封装类就不展现了,每一个人都有本身的封装方式,没有统一的标准。

Spring Cache

Spring Cache 是 Spring 为缓存场景提供的一套解决方案。经过使用 @CachePut@CacheEvict@Cacheable等注解实现对缓存的,存储、查询、删除等操做

当咱们引入了 spring-boot-starter-data-redis 后,只要在带有@Configuration类上使用 @EnableCaching 注解 Spring Cache 就会被“激活”。

Spring Cache 会为咱们配置默认的缓存管理器key生成器,可是缓存管理器对缓存的序列化和key生成器生成的key,不易阅读。建议自定义缓存管理器key生成器

若是用不上 Spring Cache ,能够不用管。

注意:Spring Cache 并非只能使用 Redis 做为缓存容器,其余例如 MemCache 等缓存中间件,都支持。

配置 Spring Cache

## spring cache 配置
# 使用的缓存的类型
spring.cache.type=redis
# 经过 spring cache 注解添加的缓存 的到期时间,单位秒(这是一个自定义属性)
cache.expireTime=60
复制代码

最重要的就是指定使用的缓存的类型
另外是一个自定义的变量,后面配置缓存管理器会用到

配置缓存管理器和 key 生成器

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import java.lang.reflect.Method;
import java.time.Duration;

@Configuration
// 开启 Spring Cache
@EnableCaching
public class CacheConfig extends CachingConfigurerSupport {

    @Value("${cache.expireTime}")
    // 缓存超时时间
    private int cacheExpireTime;

    /**
     * 配置@Cacheable、@CacheEvict等注解在没有指定Key的状况下,key生成策略
     * 该配置做用于缓存管理器管理的全部缓存
     * 最终生成的key 为 cache类注解指定的cacheNames::类名:方法名#参数值1,参数值2...
     *
     * @return
     */
    @Bean
    public KeyGenerator keyGenerator() {
        return new KeyGenerator() {
            @Override
            public Object generate(Object target, Method method, Object... params) {
                StringBuffer sb = new StringBuffer();
                sb.append(target.getClass().getName());
                sb.append(":");
                sb.append(method.getName());
                sb.append("#");
                for (Object obj : params) {
                    sb.append(obj.toString());
                    sb.append(",");
                }
                return sb.substring(0, sb.length() - 1);
            }
        };
    }


    /**
     * 配置缓存管理器
     */
    @Bean
    public CacheManager cacheManager(RedisConnectionFactory factory) {
        // 配置 json 序列化器 - Jackson2JsonRedisSerializer
        Jackson2JsonRedisSerializer<Object> jacksonSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jacksonSerializer.setObjectMapper(objectMapper);

        //关键点,spring cache 的注解使用的序列化都从这来,没有这个配置的话使用的jdk本身的序列化,实际上不影响使用,只是打印出来不适合人眼识别
        RedisCacheConfiguration cacheConfig = RedisCacheConfiguration.defaultCacheConfig()
                // 将 key 序列化成字符串
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
                // 将 value 序列化成 json
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jacksonSerializer))//value序列化方式
                // 设置缓存过时时间,单位秒
                .entryTtl(Duration.ofSeconds(cacheExpireTime))
                // 不缓存空值
                .disableCachingNullValues();

        return RedisCacheManager.builder(factory)
                .cacheDefaults(cacheConfig)
                .build();
    }
}
复制代码

总结

网上 Spring Boot 集成 redis 的教程大多都是,将 redis 和 spring cache 一块配置,很容易让人产生误解。

其实 redis 和 spring cache 是两个不一样的东西,因此,上面的教程我特地分为了两个配置文件。

你能够只使用 redis 而不使用 spring cache,也能够反过来。

那为何二者常常放在一块儿去讨论呢?
缘由在于二者也有必定的联系

站在 reids 的角度看,spring cache 提供了一种便捷的操做 reids 的途径,为缓存场景提供了优秀的解决方案。

站在 spring cache 的角度看, reids 提供了一种缓存容器,能够把缓存放在 reids 中。

缓存管理器对 reids 的操做也是经过 redisTemplate 实现的。

相关文章
相关标签/搜索