11.springboot對reids的支援
阿新 • • 發佈:2022-05-25
1.整合springdata中的redis模組
2.說明
springboot2.x之後,原底層操作redis使用的是jedis,被替換成了lettuce
為什麼呢?
jedis:採用的是直連,多個執行緒操作的話,是不安全的,如果想要避免不安全,使用jedis pool連線池!其模式更像BIO模式
lettcue:採用的是netty框架,例項可以在多個執行緒中進行共享,不存線上程不安全的情況!可以減少執行緒,更像NIO模式
3.怎麼知道springboot整合redis需要怎麼連線或者配置什麼呢
3.1springboot的自動配置:找到該檔案,這個檔案記錄了springboot自動配置的所有元件
3.2原始碼分析
1.裡面的redis內容如下: 。。。 org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration,\(redis的自動配置) org.springframework.boot.autoconfigure.data.redis.RedisReactiveAutoConfiguration,\ org.springframework.boot.autoconfigure.data.redis.RedisRepositoriesAutoConfiguration,\ 。。。 2.找到第一行redis的自動配置類: @Configuration(proxyBeanMethods = false) @ConditionalOnClass(RedisOperations.class) @EnableConfigurationProperties(RedisProperties.class)----->redis的自動配置屬性都是從這個類的屬性中獲取的! @Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class }) public class RedisAutoConfiguration { @Bean @ConditionalOnMissingBean(name = "redisTemplate")(當容器中沒有redisTemplate這個bean的時候才會生效,等於說我們可以自己寫一個redisTemplate元件替換它!) public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException { //預設redisTemplate沒有做太多的設定,redis物件都是需要序列化的 //兩個泛型都是object,我們知道reids的key都是string的,所以需要強轉為RedisTemplate<Strring, Object> RedisTemplate<Object, Object> template = new RedisTemplate<>(); template.setConnectionFactory(redisConnectionFactory); return template; } @Bean @ConditionalOnMissingBean//String是redis中最常用的,所以單獨提出來一個bean public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException { StringRedisTemplate template = new StringRedisTemplate(); template.setConnectionFactory(redisConnectionFactory); return template; } } 3.RedisProperties類中的屬性有:這些屬性都是可以在springboot的配置檔案中進行重寫的 */ @ConfigurationProperties(prefix = "spring.redis") public class RedisProperties { private int database = 0; private String url; private String host = "localhost"; private String password; private int port = 6379; private boolean ssl; private Duration timeout; private String clientName; private Sentinel sentinel; private Cluster cluster; .. } 4.springboot對redis的使用 4.1在springboot的application.properties配置redis相關資訊 spring.redis.host=192.168.2.128 spring.redis.port=6379 4.2對應的測試類: @SpringBootTest class RedisSpringbootApplicationTests { //注入redisTemplate的依賴,並通過它來操作redis @Autowired private RedisTemplate redisTemplate; @Test void contextLoads() { //獲取redis連線來清空 redisTemplate.getConnectionFactory().getConnection().flushDb(); //opsForValue用來操作字串 redisTemplate.opsForValue().set("name","吳孟達"); System.out.println(redisTemplate.opsForValue().get("name"));--->可正確輸出吳孟達 } }
執行完上述程式碼後,在linux伺服器上執行:
127.0.0.1:6379> keys *
1) "\xac\xed\x00\x05t\x00\x04name"(發現前面有部分亂碼,這是因為redis裡的物件是需要序列化的,不能直接放入redis)
自己編寫redisTemplate(儲存在redis上的資料必須序列化-->預設採用的是jdk的序列化,需要重寫下,改為:key使用StringRedisSerializer的序列化,value採用Jackson2JsonRedisSerializer序列化)
一般我們也不會直接使用redisTemplate原生api去操作reids,而是會封裝一個工具類(下面工具類可以直接使用)1.編寫一個自己的配置類: @Configuration public class RedisConfig { @Bean(方法名必須是:redisTemplate,由原始碼得知,當容器中有redisTemplate時,底層的redisTemplate會不起作用) public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory){ //為了自己開發方便,我們一般使用<String, Object> RedisTemplate<String, Object> template=new RedisTemplate<>(); template.setConnectionFactory(factory); //json序列化 Jackson2JsonRedisSerializer jackson2JsonRedisSerializer=new Jackson2JsonRedisSerializer(Object.class); ObjectMapper om=new ObjectMapper(); om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); //String序列化設定 StringRedisSerializer stringRedisSerializer=new StringRedisSerializer(); //key採用string的序列化 template.setKeySerializer(stringRedisSerializer); //hash的可以也採用string序列化 template.setHashKeySerializer(stringRedisSerializer); //value採用jackson template.setValueSerializer(jackson2JsonRedisSerializer); //hash的value序列化採用jackson template.setHashValueSerializer(jackson2JsonRedisSerializer); template.afterPropertiesSet(); return template; } } 2.實體類實現序列化:這裡使用了:lombk 說明:lombok外掛的作用是使用註解代替了繁瑣的get/set方法等,但是弊端是:團隊開發時,所有人都得安裝lombok外掛,要不彼此間有方法呼叫時,可能會報方法找不到錯誤 使用步驟: 1.idea安裝lombok外掛 2.專案匯入lombk的jar包: <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> 實體類如下 @Component @Data(註解在類上,將類提供的所有屬性都新增get、set方法,並新增、equals、canEquals、hashCode、toString方法) @AllArgsConstructor(建立一個全參建構函式) @NoArgsConstructor(建立一個無參建構函式) public class Person /*implements Serializable*/ { private String name; private Integer age; } 3.測試: @SpringBootTest class RedisSpringbootApplicationTests { @Autowired @Qualifier("redisTemplate")--->確保這裡使用的是自己重寫的redisTemplate private RedisTemplate redisTemplate; @Test public void test(){ redisTemplate.getConnectionFactory().getConnection().flushDb(); Person person=new Person("吳孟達",18);--->可以直接建立物件 redisTemplate.opsForValue().set("user",person);-->將新建的物件直接放入redis中,按照redisTemplate序列化,value會使用jackson序列化 System.out.println(redisTemplate.opsForValue().get("user")); //此處正常輸出:{"name":"吳孟達","age":18} } } 4.伺服器上 1.採用redis-cli命令登入redis時 127.0.0.1:6379> keys * 1) "user" 127.0.0.1:6379> get user "{\"name\":\"\xe5\x90\xb4\xe5\xad\x9f\xe8\xbe\xbe\",\"age\":18}"(發現值為16進位制數) 2.解決上述問題: 採用:redis-cli --raw命令登入 root@cd8a28bf1a1c:/data# redis-cli --raw 127.0.0.1:6379> keys * user 127.0.0.1:6379> get user {"name":"吳孟達","age":18}(可正常輸出)
該工具類中封裝了大量的redis操作,可以直接複製過去直接用,若沒有想要的操作,再新增即可,這樣不用每次都使用:redisTemplate.opsxxx().方法
@Component
public class RedisUtil {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
/**
*指定快取失效時間
* @param key 建
* @param time 儲存時間(秒)
* @return
*/
public boolean expire(String key,long time){
try {
if (time>0){
redisTemplate.expire(key,time, TimeUnit.SECONDS);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 獲取建的快取時間
* @param key:建,不能為null
* @return:秒 返回0代表長期有效
*/
public long getExpire(String key){
return redisTemplate.getExpire(key);
}
/**
* 判斷key是否存在
* @param key:建
* @return:true 代表存在 false不存在
*/
public boolean hasKey(String key){
try {
return redisTemplate.hasKey(key);
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 刪除快取
* @param key 建:可以傳入單個或多個
*/
public void del(String... key) {
if (key != null && key.length > 0) {
if (key.length > 0) {
redisTemplate.delete(key[0]);
} else {
redisTemplate.delete(CollectionUtils.arrayToList(key));
}
}
}
/**
* 普通快取獲取
*
* @param key:建
* @return:值
*/
public Object get(String key) {
return key == null ? null : redisTemplate.opsForValue().get(key);
}
/**
* 普通快取放入
* @param key:建
* @param value:值
* @return
*/
public boolean set(String key, Object value) {
try {
redisTemplate.opsForValue().set(key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 普通快取放入,並設定時間
* @param key
* @param value
* @param time:快取時間 秒
* @return
*/
public boolean set(String key,Object value,long time){
try {
if(time>0){
redisTemplate.opsForValue().set(key,value,time, TimeUnit.SECONDS);
}else{
set(key, value);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 遞增
* @param key
* @param detla:遞增因子 >0
* @return
*/
public long incr(String key,long detla){
if(detla<0){
throw new RuntimeException("遞增因子小於0");
}
return redisTemplate.opsForValue().increment(key,detla);
}
/**
* 遞減
* @param key:
* @param delta:遞減因子 必須大於0
* @return
*/
public long decr(String key,long delta){
if (delta<0){
throw new RuntimeException("遞減因子必須大於0");
}
return redisTemplate.opsForValue().increment(key,-delta);
}
//======================================map===============================
/**
* HashGet
* @param key 鍵 不能為null
* @param item 項 不能為null
*/
public Object hget(String key, String item) {
return redisTemplate.opsForHash().get(key, item);
}
/**
* 獲取hashKey對應的所有鍵值
* @param key 鍵
* @return 對應的多個鍵值
*/
public Map<Object, Object> hmget(String key) {
return redisTemplate.opsForHash().entries(key);
}
/**
* HashSet
* @param key 鍵
* @param map 對應多個鍵值
*/
public boolean hmset(String key, Map<String, Object> map) {
try {
redisTemplate.opsForHash().putAll(key, map);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* HashSet 並設定時間
* @param key 鍵
* @param map 對應多個鍵值
* @param time 時間(秒)
* @return true成功 false失敗
*/
public boolean hmset(String key, Map<String, Object> map, long time) {
try {
redisTemplate.opsForHash().putAll(key, map);
if (time > 0) {
expire(key, time);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 向一張hash表中放入資料,如果不存在將建立
*
* @param key 鍵
* @param item 項
* @param value 值
* @return true 成功 false失敗
*/
public boolean hset(String key, String item, Object value) {
try {
redisTemplate.opsForHash().put(key, item, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 向一張hash表中放入資料,如果不存在將建立
*
* @param key 鍵
* @param item 項
* @param value 值
* @param time 時間(秒) 注意:如果已存在的hash表有時間,這裡將會替換原有的時間
* @return true 成功 false失敗
*/
public boolean hset(String key, String item, Object value, long time) {
try {
redisTemplate.opsForHash().put(key, item, value);
if (time > 0) {
expire(key, time);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 刪除hash表中的值
*
* @param key 鍵 不能為null
* @param item 項 可以使多個 不能為null
*/
public void hdel(String key, Object... item) {
redisTemplate.opsForHash().delete(key, item);
}
/**
* 判斷hash表中是否有該項的值
*
* @param key 鍵 不能為null
* @param item 項 不能為null
* @return true 存在 false不存在
*/
public boolean hHasKey(String key, String item) {
return redisTemplate.opsForHash().hasKey(key, item);
}
/**
* hash遞增 如果不存在,就會建立一個 並把新增後的值返回
*
* @param key 鍵
* @param item 項
* @param by 要增加幾(大於0)
*/
public double hincr(String key, String item, double by) {
return redisTemplate.opsForHash().increment(key, item, by);
}
/**
* hash遞減
*
* @param key 鍵
* @param item 項
* @param by 要減少記(小於0)
*/
public double hdecr(String key, String item, double by) {
return redisTemplate.opsForHash().increment(key, item, -by);
}
// ============================set=============================
/**
* 根據key獲取Set中的所有值
* @param key 鍵
*/
public Set<Object> sGet(String key) {
try {
return redisTemplate.opsForSet().members(key);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 根據value從一個set中查詢,是否存在
*
* @param key 鍵
* @param value 值
* @return true 存在 false不存在
*/
public boolean sHasKey(String key, Object value) {
try {
return redisTemplate.opsForSet().isMember(key, value);
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 將資料放入set快取
*
* @param key 鍵
* @param values 值 可以是多個
* @return 成功個數
*/
public long sSet(String key, Object... values) {
try {
return redisTemplate.opsForSet().add(key, values);
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 將set資料放入快取
*
* @param key 鍵
* @param time 時間(秒)
* @param values 值 可以是多個
* @return 成功個數
*/
public long sSetAndTime(String key, long time, Object... values) {
try {
Long count = redisTemplate.opsForSet().add(key, values);
if (time > 0) {
expire(key, time);
}
return count;
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 獲取set快取的長度
*
* @param key 鍵
*/
public long sGetSetSize(String key) {
try {
return redisTemplate.opsForSet().size(key);
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 移除值為value的
*
* @param key 鍵
* @param values 值 可以是多個
* @return 移除的個數
*/
public long setRemove(String key, Object... values) {
try {
Long count = redisTemplate.opsForSet().remove(key, values);
return count;
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
// ===============================list=================================
/**
* 獲取list快取的內容
*
* @param key 鍵
* @param start 開始
* @param end 結束 0 到 -1代表所有值
*/
public List<Object> lGet(String key, long start, long end) {
try {
return redisTemplate.opsForList().range(key, start, end);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 獲取list快取的長度
*
* @param key 鍵
*/
public long lGetListSize(String key) {
try {
return redisTemplate.opsForList().size(key);
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 通過索引 獲取list中的值
*
* @param key 鍵
* @param index 索引 index>=0時, 0 表頭,1 第二個元素,依次類推;index<0時,-1,表尾,-2倒數第二個元素,依次類推
*/
public Object lGetIndex(String key, long index) {
try {
return redisTemplate.opsForList().index(key, index);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 將list放入快取
*
* @param key 鍵
* @param value 值
*/
public boolean lSet(String key, Object value) {
try {
redisTemplate.opsForList().rightPush(key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 將list放入快取
* @param key 鍵
* @param value 值
* @param time 時間(秒)
*/
public boolean lSet(String key, Object value, long time) {
try {
redisTemplate.opsForList().rightPush(key, value);
if (time > 0) {
expire(key, time);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 將list放入快取
*
* @param key 鍵
* @param value 值
* @return
*/
public boolean lSet(String key, List<Object> value) {
try {
redisTemplate.opsForList().rightPushAll(key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 將list放入快取
*
* @param key 鍵
* @param value 值
* @param time 時間(秒)
* @return
*/
public boolean lSet(String key, List<Object> value, long time) {
try {
redisTemplate.opsForList().rightPushAll(key, value);
if (time > 0) {
expire(key, time);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 根據索引修改list中的某條資料
*
* @param key 鍵
* @param index 索引
* @param value 值
* @return
*/
public boolean lUpdateIndex(String key, long index, Object value) {
try {
redisTemplate.opsForList().set(key, index, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 移除N個值為value
*
* @param key 鍵
* @param count 移除多少個
* @param value 值
* @return 移除的個數
*/
public long lRemove(String key, long count, Object value) {
try {
Long remove = redisTemplate.opsForList().remove(key, count, value);
return remove;
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
}
測試:
測試程式碼:
@Autowired
private RedisUtil redisUtil;
@Test
public void test1() throws InterruptedException {
redisUtil.set("wmd","吳孟達");
System.out.println(redisUtil.get("wmd"));
redisUtil.expire("wmd", 50);
while (redisUtil.getExpire("wmd")>0){
System.out.println(redisUtil.getExpire("wmd"));
Thread.sleep(2000);
}
}
輸出:
吳孟達
50
48
46
44