當前報道:【SpringBoot實戰專題】「開發實戰系列」從零開始教你舒服的使用RedisTemplate操作Redis數據

2023-01-11 14:14:38 來源:51CTO博客

SpringBoot快速操作Redis數據

在SpringBoot框架中提供了spring-boot-starter-data-redis的依賴組件進行操作Redis服務,當引入了該組件之后,只需要配置Redis的配置即可進行鏈接Redis服務并且進行操作Redis服務數據。


(相關資料圖)

針對于不同的版本有了不同的底層客戶端的支持的底層客戶端框架是不同的:目前常見的客戶端為Jedis和Lettuce。

低版本SpringBoot支持的Jedis

Jedis是很常用的Redis的Java 實現的客戶端。支持基本的數據類型如:String、Hash、List、Set、Sorted Set。

特點:使用阻塞的 I/O,方法調用同步,程序流需要等到 socket 處理完 I/O 才能執行,不支持異步操作。Jedis 客戶端實例不是線程安全的,需要通過連接池來使用 Jedis。

高版本版本SpringBoot支持的Lettuce

Lettuce客戶端主要用于線程安全同步,異步和響應使用,支持集群,Sentinel,管道和編碼器。

基于 Netty 框架的事件驅動的通信層,其方法調用是異步的。Lettuce 的 API 是線程安全的,所以可以操作單個 Lettuce 連接來完成各種操作。

spring-data-redis針對jedis提供了如下功能:

Spring Boot 的 spring-boot-starter-data-redis 為 Redis 的相關操作提供了一個高度封裝的 RedisTemplate 類,而且對每種類型的數據結構都進行了歸類,實現連接池自動管理,提供了一個高度封裝的“RedisTemplate”類。 針對jedis/Lettuce客戶端中大量api進行了歸類封裝,將同一類型操作封裝為operation接口。

通用的接口類型工廠方法

提供了對key的“bound”(綁定)便捷化操作API,可以通過bound封裝指定的key,然后進行一系列的操作而無須“顯式”的再次指定Key,即BoundKeyOperations:

ValueOperations - BoundValueOperations:String類型的簡單K-V操作SetOperations - BoundSetOperations:set類型數據操作ZSetOperations - BoundListOperations:zset類型數據操作HashOperations - BoundSetOperations:針對map類型的數據操作ListOperations - BoundHashOperations:針對list類型的數據操作
序列化/反序列化的擴展機制

針對數據的“序列化/反序列化”,提供了多種可選擇策略(RedisSerializer)

JdkSerializationRedisSerializer

POJO對象的存取場景,使用JDK本身序列化機制,將pojo類通過ObjectInputStream/ObjectOutputStream進行序列化操作,最終redis-server中將存儲字節序列。是目前最常用的序列化策略。

StringRedisSerializer

Key或者value為字符串的場景,根據指定的charset對數據的字節序列編碼成string,是“new String(bytes, charset)”和“string.getBytes(charset)”的直接封裝。是最輕量級和高效的策略。

JacksonJsonRedisSerializer

jackson-json工具提供了javabean與json之間的轉換能力,可以將pojo實例序列化成json格式存儲在redis中,也可以將json格式的數據轉換成pojo實例。因為jackson工具在序列化和反序列化時,需要明確指定Class類型,因此此策略封裝起來稍微復雜。【需要jackson-mapper-asl工具支持】

Jackson2JsonRedisSerializer

使用Jackson庫將對象序列化為JSON字符串。優點是速度快,序列化后的字符串短小精悍,不需要實現Serializable接口。但缺點也非常致命,那就是此類的構造函數中有一個類型參數,必須提供要序列化對象的類型信息(.class對象)。 通過查看源代碼,發現其只在反序列化過程中用到了類型信息。

OxmSerializer

提供了將javabean與xml之間的轉換能力,目前可用的三方支持包括jaxb,apache-xmlbeans;redis存儲的數據將是xml工具。不過使用此策略,編程將會有些難度,而且效率最低;不建議使用。【需要spring-oxm模塊的支持】

擴展第三方序列化工具

當然了除了以上這幾種基本的序列化器之外您還可以進行自定義一些更加優秀、速度更塊的序列化方式,例如:FastJsonRedisSerializer和KryoRedisSerializer、FSTRedisSerializer等。

RedisSerializer接口

RedisSerializer 基礎接口定義了將對象轉換為字節數組(二進制數據)的序列化和反序列化方法。建議將實現設計為在序列化和反序列化端處理空對象/空字節數組。注意,Redis 不接受空鍵或空值,但可以返回 null(對于不存在的鍵)。

RedisSerializer 接口方法定義
序列化

序列化方法定義如下:

byte[] serialize(T t)

該方法將給定對象 t 序列化為二進制數據,及字節數組。注意:對象 t 和返回值可以為 null。

反序列化

反序列化方法定義如下:

T deserialize(byte[] bytes)

該方法將從給定的二進制數據(字節數組)反序列化為一個對象。注意:bytes 字節數組和返回值 T 均可以為 null。

注意:如果上面的 serialize() 和 deserialize() 方法在執行時報錯,將拋出org.springframework.data.redis.serializer.SerializationException 異常。

引入spring-boot-starter-data-redis組件

springboot 與redis的整合,pom文件,依賴如下:

      org.springframework.boot      spring-boot-starter-data-redis

配置對應的application.properties文件

針對于配置我們按照jedis的配置為基礎案例,如下所示。

# Redis數據庫索引(默認為0)spring.redis.database=0  # Redis服務器地址spring.redis.host=127.0.0.1# Redis服務器連接端口spring.redis.port=6379  # Redis服務器連接密碼(默認為空)spring.redis.password=# 連接池最大連接數(使用負值表示沒有限制)spring.redis.pool.max-active=8  # 連接池最大阻塞等待時間(使用負值表示沒有限制)spring.redis.pool.max-wait=-1  # 連接池中的最大空閑連接spring.redis.pool.max-idle=8  # 連接池中的最小空閑連接spring.redis.pool.min-idle=0  # 連接超時時間(毫秒)spring.redis.timeout=0

對應的SpringBoot-Redis的核心配置類

此處需要定義RedisTemplate對象的配置類,其中需要配置對應的RedisConnectionFactory對象類以及對應類型的序列化和反序列化組件起。如下所示

定義對應的redisTemplate對象類

默認是JDK的序列化策略,這里配置redisTemplate采用的是Jackson2JsonRedisSerializer的序列化策略,參數為redisConnectionFactory。

@Bean    public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory){        //使用Jackson2JsonRedisSerializer來序列化和反序列化redis的value值(默認使用JDK的序列化方式)        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);        ObjectMapper om = new ObjectMapper();        // 指定要序列化的域,field,get和set,以及修飾符范圍,ANY是都有包括private和public        om.setVisibility(PropertyAccessor.ALL,JsonAutoDetect.Visibility.ANY);        // 指定序列化輸入的類型,類必須是非final修飾的,final修飾的類,比如String,Integer等會拋出異常        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);        jackson2JsonRedisSerializer.setObjectMapper(om);        RedisTemplate redisTemplate = new RedisTemplate<>();        // 配置連接工廠        redisTemplate.setConnectionFactory(redisConnectionFactory);        //使用StringRedisSerializer來序列化和反序列化redis的key值        //redisTemplate.setKeySerializer(new StringRedisSerializer());        redisTemplate.setKeySerializer(jackson2JsonRedisSerializer);        // 值采用json序列化        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);        redisTemplate.setHashKeySerializer(jackson2JsonRedisSerializer);        redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);        redisTemplate.afterPropertiesSet();        return redisTemplate;    }
定義對應的StringRedisTemplate對象類

但是對于 string 類型的數據,Spring Boot 還專門提供了 StringRedisTemplate 類,而且官方也建議使用該類來操作 String 類型的數據。stringRedisTemplate默認采用的是String的序列化策略。

@Bean    public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory){        StringRedisTemplate stringRedisTemplate = new StringRedisTemplate();        stringRedisTemplate.setConnectionFactory(redisConnectionFactory);        return stringRedisTemplate;    }
StringRedisTemplate和 RedisTemplate 又有啥區別呢?
RedisTemplate 是一個泛型類,而 StringRedisTemplate 不是,后者只能對鍵和值都為 String 類型的數據進行操作,而前者則可以操作任何類型。兩者的數據是不共通的,StringRedisTemplate 只能管理 StringRedisTemplate 里面的數據,RedisTemplate 只能管理 RedisTemplate 中 的數據。
定義組合序列化方式

key采用String序列化,value使用jackson序列化,如下代碼所示。

@Bean    public RedisTemplate redisTemplate(RedisConnectionFactory factory) {        RedisTemplate template = new RedisTemplate();        template.setConnectionFactory(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);        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();        // key采用String的序列化方式        template.setKeySerializer(stringRedisSerializer);        // hash的key也采用String的序列化方式        template.setHashKeySerializer(stringRedisSerializer);        // value序列化方式采用jackson        template.setValueSerializer(jackson2JsonRedisSerializer);        // hash的value序列化方式采用jackson        template.setHashValueSerializer(jackson2JsonRedisSerializer);        template.afterPropertiesSet();        return template;    }
定義RedisTemplate的腳手架

將形成一個快速操作數據的工具類,將各種類型的操作類進行封裝處理控制。

HashOperations:對hash類型的數據操作ValueOperations:對redis字符串類型數據操作ListOperations:對鏈表類型的數據操作SetOperations:對無序集合類型的數據操作ZSetOperations:對有序集合類型的數據操作

將以上各種類型的類直接進行暴漏,減少調用鏈路的路徑長度。

/**     * 對hash類型的數據操作     * @param redisTemplate     * @return     */    @Bean    public HashOperations hashOperations(RedisTemplate redisTemplate) {        return redisTemplate.opsForHash();    }    /**     * 對redis字符串類型數據操作     * @param redisTemplate     * @return     */    @Bean    public ValueOperations valueOperations(RedisTemplate redisTemplate) {        return redisTemplate.opsForValue();    }    /**     * 對鏈表類型的數據操作     * @param redisTemplate     * @return     */    @Bean    public ListOperations listOperations(RedisTemplate redisTemplate) {        return redisTemplate.opsForList();    }    /**     * 對無序集合類型的數據操作     * @param redisTemplate     * @return     */    @Bean    public SetOperations setOperations(RedisTemplate redisTemplate) {        return redisTemplate.opsForSet();    }    /**     * 對有序集合類型的數據操作     * @param redisTemplate     * @return     */    @Bean    public ZSetOperations zSetOperations(RedisTemplate redisTemplate) {        return redisTemplate.opsForZSet();    }
定義基礎層的操作處理定義RedisSupport

除了以上這幾種類型的操作之外,還有一些基礎相關的核心操作類,包含重命名,轉移以及情況整個庫的操作、設置TTL生命周期等。

@Componentpublic class RedisSupport {    @Autowired    private RedisTemplate redisTemplate;    /**     * 默認過期時長,單位:秒     */    public static final long DEFAULT_EXPIRE = 60 * 60 * 24;    /**     * 不設置過期時長     */    public static final long NOT_EXPIRE = -1;    public boolean existsKey(String key) {        return redisTemplate.hasKey(key);    }    /**     * 重名名key,如果newKey已經存在,則newKey的原值被覆蓋     *     * @param oldKey     * @param newKey     */    public void renameKey(String oldKey, String newKey) {        redisTemplate.rename(oldKey, newKey);    }    /**     * newKey不存在時才重命名     *     * @param oldKey     * @param newKey     * @return 修改成功返回true     */    public boolean renameKeyNotExist(String oldKey, String newKey) {        return redisTemplate.renameIfAbsent(oldKey, newKey);    }    /**     * 刪除key     *     * @param key     */    public void deleteKey(String key) {        redisTemplate.delete(key);    }    /**     * 刪除多個key     *     * @param keys     */    public void deleteKey(String... keys) {        Set kSet = Stream.of(keys).map(k -> k).collect(Collectors.toSet());        redisTemplate.delete(kSet);    }    /**     * 刪除Key的集合     *     * @param keys     */    public void deleteKey(Collection keys) {        Set kSet = keys.stream().map(k -> k).collect(Collectors.toSet());        redisTemplate.delete(kSet);    }    /**     * 設置key的生命周期     *     * @param key     * @param time     * @param timeUnit     */    public void expireKey(String key, long time, TimeUnit timeUnit) {        redisTemplate.expire(key, time, timeUnit);    }    /**     * 指定key在指定的日期過期     *     * @param key     * @param date     */    public void expireKeyAt(String key, Date date) {        redisTemplate.expireAt(key, date);    }    /**     * 查詢key的生命周期     *     * @param key     * @param timeUnit     * @return     */    public long getKeyExpire(String key, TimeUnit timeUnit) {        return redisTemplate.getExpire(key, timeUnit);    }    /**     * 將key設置為永久有效     *     * @param key     */    public void persistKey(String key) {        redisTemplate.persist(key);    }}

至此整體對應的RedisTemplate對象的封裝和擴展就到這里,可以把代碼介入到你的項目里面,非常方便的進行操作Redis了,是不是很OK呢?

標簽: 反序列化 集合類型 二進制數

上一篇:環球焦點!Jenkins實現代碼的部署&回滾
下一篇:【快播報】SGX性能實驗