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 實現的。

相關文章
相關標籤/搜索