前言
説起Nosql數據庫,或者數據庫緩存,消息隊列,token認証等等應用時,Redis總是大家繞不開的話題。作爲一款由C語言開髮的運行在內存中進行數據讀冩的應用,它以高性能和易用性著稱。得益於單線程的設計,避免了多線程場景下的鎖競爭,從而能做到每秒數十萬次的讀冩操作。同時,Redis還實現了主從模式,哨兵模式和集群模式等分佈式解決方案,在保証高性能的前提下提高了可用性和擴展性,成爲了開髮時不可或缺的組件。
Spring Boot 整合 Redis
由於Spring Boot中各種starter的存在,配置redis其實是簡單的事情。
首先在pom中導入依賴
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>
<dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> </dependency>
<dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-pool2</artifactId> </dependency>
|
spring-boot-starter-data-redis是redis的spring boot依賴,其中包含了lettuce客戶端。redis的客戶端可以選擇lettuce或jedis,前者是默認的,而且支持異步,在並髮場景下的性能更好,推薦使用。
jackson-databind這個是jackson的依賴,用於之後配置redis的序列化。
commons-pool2用於配置redis集群。
接下來是redis的config
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| @Configuration public class RedisConfig {
@Bean(name = "myRedisTemplate") public RedisTemplate<String, Object> getRedisTemplate(LettuceConnectionFactory lettuceConnectionFactory) { RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>(); redisTemplate.setConnectionFactory(lettuceConnectionFactory); StringRedisSerializer stringRedisSerializer = new StringRedisSerializer(); GenericJackson2JsonRedisSerializer genericJackson2JsonRedisSerializer = getSerializer();
redisTemplate.setKeySerializer(stringRedisSerializer); redisTemplate.setValueSerializer(genericJackson2JsonRedisSerializer); redisTemplate.setHashKeySerializer(stringRedisSerializer); redisTemplate.setHashValueSerializer(genericJackson2JsonRedisSerializer);
return redisTemplate; }
public GenericJackson2JsonRedisSerializer getSerializer() { ObjectMapper objectMapper = new ObjectMapper(); objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL); return new GenericJackson2JsonRedisSerializer(objectMapper); } }
|
序列化配置
對於Redis自定義序列化,首先要知道爲什麼需要自定義配置,難道不主動去配置序列化就不能使用了嗎?
其實不是,redis已經提供給了我們默認的序列化方法,翻閱官方文檔就可以看到如下內容

文中説的意思是JdkSerializationRedisSerializer是RedisTemplate和RedisCache默認的序列化方法。這個默認的序列化方法也就是JDK自帶的序列化方法(實現了Serializable接口)。確實如此,如果不進行自定義配置,Redis也是可以用默認的設置進行序列化的。
但是默認方法有一個缺點,當我們將數據存入redis之後,在redis中存儲的實際上是序列化之後的格式,也就是無法直接閱讀的格式,不利於檢查和調試,下麵是一個例子。
當我們用默認的序列化向redis中添加鍵值對(“k1”, “v1”),然後啟動redis-cli查看key的是

在redis中存儲的是序列化後的形式,前麵的一堆16進製是JDK在序列化時添加的一些前綴信息,如果想獲得存入時原始的格式,隻能通過redisTemplate.opsForValue().get("k1")來獲取。
下麵來介紹一下自定義的序列化方法
GenericJackson2JsonRedisSerializer
Jackson2JsonRedisSerializer
這兩種序列化方式在序列化String,Object,Collections,JSONObject,JSONArray都完全沒有問題,唯一的區別是,當使用GenericJackson2JsonRedisSerializer時,每個對象上會加上一個@class屬性,屬性值就是對應的全類名,而後者沒有這個@class屬性,所以在使用Jackson2JsonRedisSerializer時如果強製類型轉換會報錯,所以推薦使用GenericJackson2JsonRedisSerializer。
此外,需要用一個objectMapper來配置GenericJackson2JsonRedisSerializer,否則在序列化JSONObject的時候會出錯
1
| objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
|
setVisibility方法可以設定訪問類的字段和方法的權限
1
| objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL);
|
activateDefaultTyping這個方法用於配置處理多態時能夠正確的序列化和反序列化。
LaissezFaireSubTypeValidator是一個比較寬鬆的類型驗証器,instance是一個靜態成員,實際上就是返回了一個LaissezFaireSubTypeValidator的實例
1
| public static final LaissezFaireSubTypeValidator instance = new LaissezFaireSubTypeValidator();
|
DefaultTyping指定如何處理類型信息
封裝一個RedisUtils類
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| package org.zzb.redisusage.utils;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Component;
import java.util.List; import java.util.Set;
@Component public class RedisUtils {
private final RedisTemplate<String, Object> redisTemplate;
@Autowired public RedisUtils(@Qualifier("myRedisTemplate") RedisTemplate<String, Object> redisTemplate) { this.redisTemplate = redisTemplate; }
public void set(String key, Object value) { redisTemplate.opsForValue().set(key, value); }
public Object get(String key) { return redisTemplate.opsForValue().get(key); }
public Set<String> getAllKeys() { return redisTemplate.keys("*"); } }
|
包含了簡單的set和get方法以及獲取所有的key。
測試類
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| @SpringBootTest @Slf4j class RedisUsageApplicationTests {
@Autowired RedisUtils redisUtils;
@Test void contextLoads() throws JSONException { String JavaPojo = "JavaPojo"; Person person01 = new Person("zzb", "English"); redisUtils.set(JavaPojo, person01);
String JSONObjectKey = "JSONObjectKey"; JSONObject jsonObject = new JSONObject(); jsonObject.put("name", "zzb"); jsonObject.put("age", 23); redisUtils.set(JSONObjectKey, jsonObject);
String JavaString = "JavaString"; redisUtils.set(JavaString, "v1");
String JavaList = "JavaList"; List<Person> list = new ArrayList<>(); list.add(person01); redisUtils.set(JavaList, list);
String JSONArray = "JSONArray"; JSONArray jsonArray = new JSONArray(); jsonArray.put(person01); redisUtils.set(JSONArray, jsonArray);
log.info("JavaPojo -> {}", redisUtils.get(JavaPojo)); log.info("JavaString -> {}", redisUtils.get(JavaString)); log.info("JSONObject -> {}", redisUtils.get(JSONObjectKey)); log.info("ArrayList -> {}", redisUtils.get(JavaList)); log.info("JSONArray -> {}", redisUtils.get(JSONArray));
log.info("All keys -> {}", redisUtils.getAllKeys()); } }
|
Output
1 2 3 4 5 6
| 2024-09-02T16:46:24.048+08:00 INFO 61606 --- [redis-usage] [ main] o.z.r.RedisUsageApplicationTests : JavaPojo -> Person(name=zzb, language=English) 2024-09-02T16:46:24.050+08:00 INFO 61606 --- [redis-usage] [ main] o.z.r.RedisUsageApplicationTests : JavaString -> v1 2024-09-02T16:46:24.056+08:00 INFO 61606 --- [redis-usage] [ main] o.z.r.RedisUsageApplicationTests : JSONObject -> {"name":"zzb","age":23} 2024-09-02T16:46:24.059+08:00 INFO 61606 --- [redis-usage] [ main] o.z.r.RedisUsageApplicationTests : ArrayList -> [Person(name=zzb, language=English)] 2024-09-02T16:46:24.061+08:00 INFO 61606 --- [redis-usage] [ main] o.z.r.RedisUsageApplicationTests : JSONArray -> ["Person(name=zzb, language=English)"] 2024-09-02T16:46:24.066+08:00 INFO 61606 --- [redis-usage] [ main] o.z.r.RedisUsageApplicationTests : All keys -> [JSONObjectKey, JavaString, JavaPojo, JSONArray, JavaList]
|
在redis-cli中嚐試獲取剛才的數據
美化一下JSON之後得到的輸出(字符串v1就不展示了)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
| [ "org.zzb.redisusage.pojo.Person", { "name": "zzb", "language": "English" } ]
[ "org.json.JSONObject", { "nameValuePairs": [ "java.util.HashMap", { "name": "zzb", "age": 23 } ] } ]
[ "java.util.ArrayList", [ [ "org.zzb.redisusage.pojo.Person", { "name": "zzb", "language": "English" } ] ] ]
[ "org.json.JSONArray", { "values": [ "java.util.ArrayList", [ [ "org.zzb.redisusage.pojo.Person", { "name": "zzb", "language": "English" } ] ] ] } ]
|