1. 程式人生 > 其它 >11.springboot對reids的支援

11.springboot對reids的支援

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序列化)
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}(可正常輸出)
一般我們也不會直接使用redisTemplate原生api去操作reids,而是會封裝一個工具類(下面工具類可以直接使用)
該工具類中封裝了大量的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