SpringBoot系列之Redis緩存使用詳細教程

介紹SpringBoot項目中使用緩存,之前先介紹一下Spring的緩存抽象和JSR107,本博客是我在學習尚硅谷視頻和參考其它博客之後做的筆記,僅供學習參考,本博客會比較詳細地介紹Spring的緩存註解,以及如何在Springboot項目中使用這些註解實現緩存,同時也會介紹怎麼引入Redis實現緩存,僅做學習參考

一、Spring的緩存抽象

1.1、緩存抽象定義

Spring從3.1開始定義了org.springframework.cache.Cache
和org.springframework.cache.CacheManager接口來統一不同的緩存技術;並支持使用Java Caching(JSR-107)註解簡化我們進行緩存開發。Spring Cache 只負責維護抽象層,具體的實現由你的技術選型來決定。將緩存處理和緩存技術解除耦合。

1.2、重要接口

  • Cache:緩存抽象的規範接口,緩存實現有:RedisCache、EhCacheCache、ConcurrentMapCache等
  • CacheManager:緩存管理器,管理Cache的生命週期

二、JSR107

2.1、JSR107核心接口

Java Caching(JSR-107)定義了5個核心接口,分別是CachingProvider, CacheManager, Cache, Entry和 Expiry。

  • CachingProvider:創建、配置、獲取、管理和控制多個CacheManager
  • CacheManager:創建、配置、獲取、管理和控制多個唯一命名的Cache,Cache存在於CacheManager的上下文中。一個CacheManager僅對應一個CachingProvider
  • Cache:是由CacheManager管理的,CacheManager管理Cache的生命週期,Cache存在於CacheManager的上下文中,是一個類似map的數據結構,並臨時存儲以key爲索引的值。一個Cache僅被一個CacheManager所擁有
  • Entry:是一個存儲在Cache中的key-value對
  • Expiry:每一個存儲在Cache中的條目都有一個定義的有效期。一旦超過這個時間,條目就自動過期,過期後,條目將不可以訪問、更新和刪除操作。緩存有效期可以通過ExpiryPolicy設置

2.2、JSR107圖示

引用尚硅谷視頻課件中的圖示:
在這裏插入圖片描述

三、Spring緩存使用

3.1、重要註解簡介

例子實踐之前,先簡單介紹Spring提供的重要緩存註解

  • @Cacheable:針對方法配置,能夠根據方法的請求參數對其結果進行緩存
  • @CacheEvict:清空緩存
  • @CachePut:既調用方法,又更新緩存數據
  • @EnableCaching:開啓基於註解的緩存
  • @Caching:定義複雜的緩存規則

3.2、環境準備

ok,本博客以尚硅谷視頻例子進行改寫,用這個比較經典的例子進行說明

環境準備:

  • maven環境
  • IntelliJ IDEA
    新建兩張表:
DROP TABLE IF EXISTS `employee`;
CREATE TABLE `employee` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `lastName` varchar(255) DEFAULT NULL,
  `email` varchar(255) DEFAULT NULL,
  `gender` int(2) DEFAULT NULL,
  `d_id` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

DROP TABLE IF EXISTS `department`;
CREATE TABLE `department` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `departmentName` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

3.3、引入spring-boot-starter-cache模塊

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-cache</artifactId>
        </dependency>

3.4、主要註解例子實踐

3.4.1、@EnableCaching

@EnableCaching開啓基於註解的緩存


import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;

@SpringBootApplication
@EnableCaching
public class SpringbootCacheApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringbootCacheApplication.class, args);
    }

}

3.4.2、@Cacheable註解

@Cacheable註解的作用,前面也簡介了,主要是針對方法配置,能夠根據方法的請求參數對其結果進行緩存,介紹一下註解的主要屬性

  • cacheNames/value:指定緩存組件的名字,數組形式
  • key:緩存數據使用的key,確定緩存可以用唯一key進行指定;eg:編寫SpEL; #id,參數id的值 ,,#a0(第一個參數), #p0(和a0的一樣的意義) ,#root.args[0]
  • keyGenerator:key的生成器;可以自己指定key的生成器的組件id(注意: key/keyGenerator:二選一使用;不能同時使用)
  • cacheManager:指定緩存管理器;或者cacheResolver指定獲取解析器
  • condition:指定符合條件的情況下才緩存;使用SpEl表達式,eg:condition = “#a0>1”:第一個參數的值>1的時候才進行緩存
  • unless:否定緩存;當unless指定的條件爲true,方法的返回值就不會被緩存;eg:unless = “#a0!=2”:如果第一個參數的值不是2,結果不緩存;
  • sync:是否使用異步模式
    @Cacheable(value = {"emp"}, /*keyGenerator = "myKeyGenerator",*/key = "#id",condition = "#a0>=1",unless = "#a0!=2")
    public Employee getEmp(Integer id) {
        Employee employee = this.employeeMapper.getEmpById(id);
        LOG.info("查詢{}號員工數據",id);
        return employee;
    }

這裏也可以使用自定義的keyGenerator,使用屬性keyGenerator = "myKeyGenerator

定義一個@Bean類,將KeyGenerator添加到Spring容器

@Configuration
public class CacheConfig {

    @Bean(value = {"myKeyGenerator"})
    public KeyGenerator keyGenerator(){
        return new KeyGenerator() {
            @Override
            public Object generate(Object target, Method method, Object... params) {
                return method.getName()+"["+ Arrays.asList(params).toString()+"]";
            }
        };
    }
}

3.4.3、@CachePut註解

@CachePut註解也是一個用來緩存的註解,不過緩存和@Cacheable有明顯的區別是即調用方法,又更新緩存數據,也就是執行方法操作之後再來同步更新緩存,所以這個主鍵常用於更新操作,也可以用於查詢,主鍵屬性和@Cacheable有很多類似的,詳情參看@link @CachePut源碼

 /**
     *  @CachePut:既調用方法,又更新緩存數據;同步更新緩存
     *  修改了數據,同時更新緩存
     */
    @CachePut(value = {"emp"}, key = "#result.id")
    public Employee updateEmp(Employee employee){
        employeeMapper.updateEmp(employee);
        LOG.info("更新{}號員工數據",employee.getId());
        return employee;
    }

3.4.4、 @CacheEvic註解

主要屬性:

  • key:指定要清除的數據
  • allEntries = true:指定清除這個緩存中所有的數據
  • beforeInvocation = false:默認代表緩存清除操作是在方法執行之後執行
  • beforeInvocation = true:代表清除緩存操作是在方法運行之前執行
    @CacheEvict(value = {"emp"}, beforeInvocation = true,key="#id")
    public void deleteEmp(Integer id){
        employeeMapper.deleteEmpById(id);
        //int i = 10/0;
    }

3.4.5、@Caching註解

@Caching 用於定義複雜的緩存規則,可以集成@Cacheable和 @CachePut

 // @Caching 定義複雜的緩存規則
    @Caching(
            cacheable = {
                    @Cacheable(/*value={"emp"},*/key = "#lastName")
            },
            put = {
                    @CachePut(/*value={"emp"},*/key = "#result.id"),
                    @CachePut(/*value={"emp"},*/key = "#result.email")
            }
    )
    public Employee getEmpByLastName(String lastName){
        return employeeMapper.getEmpByLastName(lastName);
    }

3.4.6、 @CacheConfig註解

@CacheConfig註解可以用於抽取緩存的公共配置,然後在類加上就可以,eg:@CacheConfig(cacheNames = {"emp"},cacheManager = "employeeCacheManager")

附錄拓展:SpEL表達式用法

Cache SpEL available metadata

名稱 位置 描述 示例
methodName root對象 當前被調用的方法名 #root.methodname
method root對象 當前被調用的方法 #root.method.name
target root對象 當前被調用的目標對象實例 #root.target
targetClass root對象 當前被調用的目標對象的類 #root.targetClass
args root對象 當前被調用的方法的參數列表 #root.args[0]
caches root對象 當前方法調用使用的緩存列表 #root.caches[0].name
argument Name 執行上下文(avaluation context) 當前被調用的方法的參數,如findArtisan(Artisan artisan),可以通過#artsian.id獲得參數 #artsian.id
result 執行上下文(evaluation context) 方法執行後的返回值(僅當方法執行後的判斷有效,如 unless cacheEvict的beforeInvocation=false) #result

四、集成Redis緩存

4.1、環境準備

基於前面的Spring緩存環境,集成redis要引入相關配置:

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
<dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-pool2</artifactId>
        </dependency>

切換緩存方式爲Redis:spring.cache.type=redis
在這裏插入圖片描述

4.2、Redis配置類實現

RedisTemplate配置

@Resource
    private LettuceConnectionFactory lettuceConnectionFactory;

    @Bean
    @Primary
    public RedisTemplate<Object,Object> redisTemplate(){
        RedisTemplate<Object,Object> redisTemplate = new RedisTemplate<Object, Object>();
        redisTemplate.setConnectionFactory(lettuceConnectionFactory);
        Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = this.initJacksonSerializer();
        // 設置value的序列化規則和 key的序列化規則
        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }

RedisCacheManager相關代碼可以參考博文,該博主已經對代碼做了比較好的封裝,所以本文不復制代碼

4.3、RestTemplate相關操作

使用RestTemplate操作redis

  • 1、 redisTemplate.opsForValue();//操作字符串
  • 2、redisTemplate.opsForHash();//操作hash
  • 3、redisTemplate.opsForList();//操作list
  • 4、redisTemplate.opsForSet();//操作set
  • 5、redisTemplate.opsForZSet();//操作有序set

4.4、緩存業務測試

@Autowired
    DepartmentMapper departmentMapper;

    @Qualifier("redisCacheManager")
    @Autowired
    RedisCacheManager redisCacheManager;

//      @Cacheable(cacheNames = "dept",cacheManager = "redisCacheManager")
//    public Department getDeptById(Integer id){
//        System.out.println("查詢部門"+id);
//        Department department = departmentMapper.getDeptById(id);
//        return department;
//    }

    // 使用緩存管理器得到緩存,進行api調用
   public Department getDeptById(Integer id){
        LOG.info("查詢id爲{}的員工信息",id);

        //獲取某個緩存
        Cache deptCache = redisCacheManager.getCache("dept");
        Department department = null;
        if(deptCache.get(id)==null){
            department = departmentMapper.getDeptById(id);
            deptCache.put(id,department);
        } else {
            SimpleValueWrapper valueWrapper = (SimpleValueWrapper) deptCache.get(id);
            department = (Department)valueWrapper.get();
        }

        return department;
    }

在這裏插入圖片描述

當然使用前面的Spring主鍵也是可以緩存的,
在這裏插入圖片描述
參考博文:
緩存抽象層Spring cache實戰操作

代碼例子下載:github鏈接

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章