SpringBoot中整合Redis(快取篇)

艾神一不小心發表於2018-06-12

實際開發中快取處理是必須的,不可能我們每次客戶端去請求一次伺服器,伺服器每次都要去資料庫中進行查詢,為什麼要使用快取?說到底是為了提高系統的執行速度。將使用者頻繁訪問的內容存放在離使用者最近,訪問速度最快的地方,提高使用者的響 應速度,今天先來講下在springboot中整合redis的詳細步驟。

一、安裝

首先要在本地安裝一個redis程式,redis下載地址,安裝過程十分簡單(略過),安裝完成後進入到redis資料夾中可以看到如下:

SpringBoot中整合Redis(快取篇)

點選redis-server.exe開啟redis服務,可以看到如下圖所示即代表開啟redis服務成功:

SpringBoot中整合Redis(快取篇)
那麼我們可以開啟redis客戶端進行測試:

SpringBoot中整合Redis(快取篇)

二、整合到springboot

1、在專案中加入redis依賴,pom檔案中新增如下:

<!-- 整合Redis快取支援 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
複製程式碼

2、在application.yml中新增redis配置:

##預設密碼為空
redis:
      host: 127.0.0.1
      # Redis伺服器連線埠
      port: 6379
      jedis:
        pool:
          #連線池最大連線數(使用負值表示沒有限制)
          max-active: 100
          # 連線池中的最小空閒連線
          max-idle: 10
          # 連線池最大阻塞等待時間(使用負值表示沒有限制)
          max-wait: 100000
      # 連線超時時間(毫秒)
      timeout: 5000
      #預設是索引為0的資料庫
      database: 0 
複製程式碼

3、新建RedisConfiguration配置類,繼承CachingConfigurerSupport,@EnableCaching開啟註解

@Configuration
@EnableCaching
public class RedisConfiguration extends CachingConfigurerSupport {
    /**
     * 自定義生成key的規則
     */
    @Override
    public KeyGenerator keyGenerator() {
        return new KeyGenerator() {
            @Override
            public Object generate(Object o, Method method, Object... objects) {
                //格式化快取key字串
                StringBuilder sb = new StringBuilder();
                //追加類名
                sb.append(o.getClass().getName());
                //追加方法名
                sb.append(method.getName());
                //遍歷引數並且追加
                for (Object obj : objects) {
                    sb.append(obj.toString());
                }
                System.out.println("呼叫Redis快取Key : " + sb.toString());
                return sb.toString();
            }
        };
    }
    
    /**
     * 採用RedisCacheManager作為快取管理器
     * @param connectionFactory
     */
    @Bean
    public CacheManager cacheManager(RedisConnectionFactory connectionFactory) {
        RedisCacheManager redisCacheManager = RedisCacheManager.create(connectionFactory);
        return  redisCacheManager;
    }

    @Bean
    public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory factory) {
        ////解決鍵、值序列化問題
        StringRedisTemplate template = new StringRedisTemplate(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);
        template.setValueSerializer(jackson2JsonRedisSerializer);
        template.afterPropertiesSet();
        return template;
    }
}
複製程式碼

4、建立自定義的介面來定義需要的redis的功能

/**
 * K 指以hash結構操作時 鍵型別
 * T 為資料實體 應實現序列化介面,並定義serialVersionUID * RedisTemplate 提供了五種資料結構操作型別 hash / list / set / zset / value
 * 方法命名格式為 資料操作型別 + 操作 如 hashPut 指以hash結構(也就是map)想key新增鍵值對 
 */
public interface RedisHelper<HK, T> {
    /**
     * Hash結構 新增元素 * @param key key * @param hashKey hashKey * @param domain 元素
     */
    void hashPut(String key, HK hashKey, T domain);

    /**
     * Hash結構 獲取指定key所有鍵值對 * @param key * @return
     */
    Map<HK, T> hashFindAll(String key);

    /**
     * Hash結構 獲取單個元素 * @param key * @param hashKey * @return
     */
    T hashGet(String key, HK hashKey);

    void hashRemove(String key, HK hashKey);

    /**
     * List結構 向尾部(Right)新增元素 * @param key * @param domain * @return
     */
    Long listPush(String key, T domain);

    /**
     * List結構 向頭部(Left)新增元素 * @param key * @param domain * @return
     */
    Long listUnshift(String key, T domain);

    /**
     * List結構 獲取所有元素 * @param key * @return
     */
    List<T> listFindAll(String key);

    /**
     * List結構 移除並獲取陣列第一個元素 * @param key * @return
     */
    T listLPop(String key);

    /**
     * 物件的實體類
     * @param key
     * @param domain
     * @return
     */
    void valuePut(String key, T domain);

    /**
     * 獲取物件實體類
     * @param key
     * @return
     */
    T getValue(String key);

    void remove(String key);

    /**
     * 設定過期時間 * @param key 鍵 * @param timeout 時間 * @param timeUnit 時間單位
     */
    boolean expirse(String key, long timeout, TimeUnit timeUnit);
}
複製程式碼

下面是建立RedisHelperImpl進行介面的實現

@Service("RedisHelper")
public class RedisHelperImpl<HK, T> implements RedisHelper<HK, T> {
    // 在構造器中獲取redisTemplate例項, key(not hashKey) 預設使用String型別
    private RedisTemplate<String, T> redisTemplate;
    // 在構造器中通過redisTemplate的工廠方法例項化操作物件
    private HashOperations<String, HK, T> hashOperations;
    private ListOperations<String, T> listOperations;
    private ZSetOperations<String, T> zSetOperations;
    private SetOperations<String, T> setOperations;
    private ValueOperations<String, T> valueOperations;

    // IDEA雖然報錯,但是依然可以注入成功, 例項化操作物件後就可以直接呼叫方法操作Redis資料庫
    @Autowired
    public RedisHelperImpl(RedisTemplate<String, T> redisTemplate) {
        this.redisTemplate = redisTemplate;
        this.hashOperations = redisTemplate.opsForHash();
        this.listOperations = redisTemplate.opsForList();
        this.zSetOperations = redisTemplate.opsForZSet();
        this.setOperations = redisTemplate.opsForSet();
        this.valueOperations = redisTemplate.opsForValue();
    }

    @Override
    public void hashPut(String key, HK hashKey, T domain) {
        hashOperations.put(key, hashKey, domain);
    }

    @Override
    public Map<HK, T> hashFindAll(String key) {
        return hashOperations.entries(key);
    }

    @Override
    public T hashGet(String key, HK hashKey) {
        return hashOperations.get(key, hashKey);
    }

    @Override
    public void hashRemove(String key, HK hashKey) {
        hashOperations.delete(key, hashKey);
    }

    @Override
    public Long listPush(String key, T domain) {
        return listOperations.rightPush(key, domain);
    }

    @Override
    public Long listUnshift(String key, T domain) {
        return listOperations.leftPush(key, domain);
    }

    @Override
    public List<T> listFindAll(String key) {
        if (!redisTemplate.hasKey(key)) {
            return null;
        }
        return listOperations.range(key, 0, listOperations.size(key));
    }

    @Override
    public T listLPop(String key) {
        return listOperations.leftPop(key);
    }

    @Override
    public void valuePut(String key, T domain) {
        valueOperations.set(key, domain);
    }

    @Override
    public T getValue(String key) {
        return valueOperations.get(key);
    }

    @Override
    public void remove(String key) {
        redisTemplate.delete(key);
    }

    @Override
    public boolean expirse(String key, long timeout, TimeUnit timeUnit) {
        return redisTemplate.expire(key, timeout, timeUnit);
    }
}
複製程式碼

三、測試

編寫TestRedis類進行測試

@RunWith(SpringRunner.class)
@SpringBootTest
public class TestRedis {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private RedisHelperImpl redisHelper;

    @Test
    public void test() throws Exception{
//        基本寫法
//        stringRedisTemplate.opsForValue().set("aaa","111");
//        Assert.assertEquals("111",stringRedisTemplate.opsForValue().get("aaa"));
//        System.out.println(stringRedisTemplate.opsForValue().get("aaa"));
        Author user=new Author();
        user.setName("Alex");
        user.setIntro_l("不會打籃球的程式不是好男人");
        redisHelper.valuePut("aaa",user);
        System.out.println(redisHelper.getValue("aaa"));
    }

    @Test
    public void testObj() throws Exception {
        Author user=new Author();
        user.setName("Jerry");
        user.setIntro_l("不會打籃球的程式不是好男人!");

        ValueOperations<String, Author> operations=redisTemplate.opsForValue();
        operations.set("502", user);
        Thread.sleep(500);
        boolean exists=redisTemplate.hasKey("502");
        if(exists){
            System.out.println(redisTemplate.opsForValue().get("502"));
        }else{
            System.out.println("exists is false");
        }
        // Assert.assertEquals("aa", operations.get("com.neo.f").getUserName());
    }
}
複製程式碼

執行TestRedis測試類,結果如下:

SpringBoot中整合Redis(快取篇)

注意:如果在RedisConfiguration中不配置redisTemplate(RedisConnectionFactory factory)註解,會造成鍵、值的一個序列化問題,有興趣的可以去試一下。

四、專案實戰

首先需要在程式的入口處Application中新增@EnableCaching開啟快取的註解

@EnableCaching  //開啟快取
@SpringBootApplication
public class PoetryApplication {

    public static void main(String[] args) {
        SpringApplication.run(PoetryApplication.class, args);
    }
}
複製程式碼

上面的redis相關寫法是我們自定義設定並獲取的,那麼我們經常要在訪問介面的地方去使用redis進行快取相關實體物件以及集合等,那麼我們怎麼實現呢?比如我現在想在AuthorController中去快取作者相關資訊的快取資料,該怎麼辦呢?如下:

@RestController
@RequestMapping(value = "/poem")
public class AuthorController {

    private final static Logger logger = LoggerFactory.getLogger(AuthorController.class);

    @Autowired
    private AuthorRepository authorRepository;

    @Cacheable(value="poemInfo")  //自動根據方法生成快取
    @PostMapping(value = "/poemInfo")
    public Result<Author> author(@RequestParam("author_id") int author_id, @RequestParam("author_name")String author_name) {
        if(StringUtils.isEmpty(author_id) || StringUtils.isEmpty(author_name)){
            return ResultUtils.error(ResultCode.INVALID_PARAM_EMPTY);
        }
        Author author;
        Optional<Author> optional = authorRepository.getAuthorByIdAndName(author_id, author_name);
        if (optional.isPresent()) {
            author = optional.get();
            //通過\n或者多個空格 進行過濾去重
            if (!StringUtils.isEmpty(author.getIntro_l())) {
                String s = author.getIntro_l();
                String intro = s.split("\\s +")[0];
                author.setIntro_l(intro);
            }
        } else {
           return ResultUtils.error(ResultCode.NO_FIND_THINGS);
        }
        return ResultUtils.ok(author);
    }
}
複製程式碼

這裡 @Cacheable(value="poemInfo")這個註解的意思就是自動根據方法生成快取,value就是快取下來的key。

到這裡我們就已經把redis整合到了springboot中了,感興趣的同學可以關注一下我們的一個開源專案Android App《最美詩詞》。

App《最美詩詞》Android端原始碼Github地址: VinsonGuo/android-poetry

App《最美詩詞》服務端原始碼(本教程)Github地址: Alex-Jerry/Java-Poetry

App《最美詩詞》APK酷安下載地址: 最美詩詞(com.tech502.poetry) - 1.0 - 應用 - 酷安網

相關文章