深入淺出 Spring Cache 使用與整合(附源碼解析)


深入淺出 Spring Cache 使用與整合(附源碼解析)

個人開發環境

java環境:Jdk1.8.0_60

編譯器:IntelliJ IDEA 2019.1

springCache官方文檔:https://docs.spring.io/spring/docs/5.1.9.RELEASE/spring-framework-reference/integration.html#cache

一、Spring緩存抽象

SpringCache產生的背景其實與Spring產生的背景有點類似。由於Java EE 系統框架臃腫、低效,代碼可觀性低,對象創建和依賴關系復雜,Spring框架出來了,目前基本上所有的Java后台項目都離不開Spring或SpringBoot(對Spring的進一步封裝簡化)。現在項目面臨高並發的問題越來越多,各類緩存的應用也增多,那么在通用的Spring框架上,就需要有一種更加便捷簡單的方式,來完成緩存的支持。
SpringCache本身是一個緩存體系的抽象實現,並沒有具體的緩存能力,要使用SpringCache還需要配合具體的緩存實現來完成。

  • Cache接口為緩存的組件規范定義,包含緩存的各種操作集合;
  • Cache接口下Spring提供了各種xxxCache的實現;如RedisCache,EhCacheCache ,ConcurrentMapCache等;

二、重要注解、參數

名稱 解釋
Cache 緩存接口,定義緩存操作。實現有:RedisCache、EhCacheCache、ConcurrentMapCache等
CacheManager 緩存管理器,管理各種緩存(cache)組件
@Cacheable 主要針對方法配置,能夠根據方法的請求參數對其進行緩存
@CacheEvict 清空緩存
@CachePut 保證方法被調用,又希望結果被緩存。 與@Cacheable區別在於是否每次都調用方法,常用於更新
@EnableCaching 開啟基於注解的緩存
keyGenerator 緩存數據時key生成策略
serialize 緩存數據時value序列化策略
@CacheConfig 統一配置本類的緩存注解的屬性

@Cacheable/@CachePut/@CacheEvict 主要的參數

名稱 解釋
value 緩存的名稱,在 spring 配置文件中定義,必須指定至少一個 例如: @Cacheable(value=”mycache”) 或者 @Cacheable(value={”cache1”,”cache2”}
key 緩存的 key,可以為空,如果指定要按照 SpEL 表達式編寫, 如果不指定,則缺省按照方法的所有參數進行組合 例如: @Cacheable(value=”testcache”,key=”#id”)
condition 緩存的條件,可以為空,使用 SpEL 編寫,返回 true 或者 false, 只有為 true 才進行緩存/清除緩存 例如:@Cacheable(value=”testcache”,condition=”#userName.length()>2”)
unless 否定緩存。當條件結果為TRUE時,就不會緩存。 @Cacheable(value=”testcache”,unless=”#userName.length()>2”)
allEntries (@CacheEvict ) 是否清空所有緩存內容,缺省為 false,如果指定為 true, 則方法調用后將立即清空所有緩存 例如: @CachEvict(value=”testcache”,allEntries=true)
beforeInvocation (@CacheEvict) 是否在方法執行前就清空,缺省為 false,如果指定為 true, 則在方法還沒有執行的時候就清空緩存,缺省情況下,如果方法 執行拋出異常,則不會清空緩存 例如: @CachEvict(value=”testcache”,beforeInvocation=true)

三、SpEL上下文數據

Spring Cache提供了一些供我們使用的SpEL上下文數據,下表直接摘自Spring官方文檔:

名稱 位置 描述 示例
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 執行上下文 當前被調用的方法的參數,如findArtisan(Artisan artisan),可以通過#artsian.id獲得參數 #artsian.id
result 執行上下文 方法執行后的返回值(僅當方法執行后的判斷有效,如 unless cacheEvict的beforeInvocation=false) #result

四、實戰

1.導入依賴

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

2.啟動類開啟緩存注解@EnableCaching

/**
 * 1、開啟基於注解的緩存 @EnableCaching
 * 2、標注緩存注解即可
 * 	  @Cacheable
 * 	  @CacheEvict
 * 	  @CachePut
 */
@SpringBootApplication
@EnableCaching  //開啟緩存
public class CacheApplication{
    public static void main(String[] args) {
        SpringApplication.run(CacheApplication.class, args);
    }

}

3.緩存 @Cacheable

運行流程:

@Cacheable方法運行之前,先去查詢Cache(緩存組件),按照cacheNames/value指定的名字獲取,第一次獲取緩存如果沒有Cache組件會自動創建。

源碼分析

public @interface Cacheable {
    // cacheNames/value:指定緩存組件的名字;將方法的返回結果放在哪個緩存中,是數組的方式,可以指定多個緩存
    @AliasFor("cacheNames")
    String[] value() default {};
    @AliasFor("value")
    String[] cacheNames() default {};

    // 緩存數據使用的key;可以用它來指定。默認是使用方法參數的值
    String key() default "";

    // key的生成器;可以自己指定key的生成器的組件id,key/keyGenerator:二選一使用;
    String keyGenerator() default "";

    // 指定緩存管理器;或者cacheResolver指定獲取解析器 作用得到緩存集合
    String cacheManager() default "";
    String cacheResolver() default "";

    // 條件符合則緩存,編寫SpEL:condition = "#a0>1":第一個參數的值大於1的時候才進行緩存
    String condition() default "";
    
    // 條件符合則不緩存,編寫SpEL:unless = "#a0==2":如果第一個參數的值是2,結果不緩存;
    String unless() default "";

    // 是否使用異步模式,是否啟用異步模式
    boolean sync() default false;
}

操練一下

@Cacheable(value = "emp" ,key = "targetClass + methodName +#p0")
public Employee getEmp(Integer id){
    System.out.println("查詢"+id+"號員工");
    Employee emp = employeeMapper.getEmpById(id);
    return emp;
}

踩坑

1、屬性value/cacheNames是必需的,它指定了你的緩存存放在哪塊命名空間。

2、屬性key是使用的spEL表達式

注意:踩坑,如果你把methodName換成method運行會報錯,觀察它們的返回類型,原因在於methodNameStringmethohMethod

Employee實體類一定要實現序列化public class Employee implements Serializable,否則會報java.io.NotSerializableException異常。

4.更新 @CachePut

@CachePut既調用方法,又更新緩存數據;同步更新緩存。簡單來說就是用戶修改數據同步更新緩存數據。

源碼分析:(皮一下,去事故與@Cacheable相同,不做過多解釋了,狗頭護體~~~)

public @interface CachePut {
    
    @AliasFor("cacheNames")
    String[] value() default {};
    
    @AliasFor("value")
    String[] cacheNames() default {};

    String key() default "";

    String keyGenerator() default "";

    String cacheManager() default "";

    String cacheResolver() default "";

    String condition() default "";

    String unless() default "";
}

操練一下

注意該注解的value/cahceNameskey 必須與要更新的緩存相同,也就是與@Cacheable 相同。

@CachePut(value = "emp" ,key = "#employee.id")
public Employee updateEmp(Employee employee){
    System.out.println("updateEmp:"+employee);
    employeeMapper.updateEmp(employee);
    return employee;
}

5.清除 @CacheEvict

@CachEvict 的作用 主要針對方法配置,能夠根據一定的條件對緩存進行清空 。

源碼分析

public @interface CacheEvict {
    // 同上同上同上
    @AliasFor("cacheNames")
    String[] value() default {};
    @AliasFor("value")
    String[] cacheNames() default {};
    String key() default "";
    String keyGenerator() default "";
    String cacheManager() default "";
    String cacheResolver() default "";
    String condition() default "";

    // 指定清除這個緩存中所有的數據,示例:@CachEvict(value=”emp”,allEntries=true)
    boolean allEntries() default false;

    /*
     * 示例: @CachEvict(value=”emp”,beforeInvocation=true)
     * 代表清除緩存操作是在方法運行之前執行,無論方法是否出現異常,緩存都清除
     *
     * 示例: @CachEvict(value=”emp”,beforeInvocation=false)(默認)
     * 緩存的清除是否在方法之前執行 , 默認代表緩存清除操作是在方法執行之后執行;如果出現異常緩存就不會清除
     */
    boolean beforeInvocation() default false;
}

操練一下

//方法調用后清空所有緩存
@CacheEvict(value="accountCache", allEntries=true)
public void deleteEmp() {
	employeeMapper.deleteAll();
}

6.組合 @Caching

有時候我們可能組合多個Cache注解使用,此時就需要@Caching組合多個注解標簽了。

源碼分析

public @interface Caching {
    // 用於指定多個緩存設置操作
    Cacheable[] cacheable() default {};
    
    // 用於指定多個緩存更新操作
    CachePut[] put() default {};
    
    // 用於指定多個緩存失效操作
    CacheEvict[] evict() default {};
}

操練一下

// @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);
}

7.全局配置 @CacheConfig

當我們需要緩存的地方越來越多,可以使用@CacheConfig(cacheNames = {"emp"})注解來統一指定value的值,這時可省略value,如果你在你的方法依舊寫上了value,那么依然以方法的value值為准。

源碼分析

public @interface Caching {
    // 用於指定多個緩存設置操作
    Cacheable[] cacheable() default {};
    
    // 用於指定多個緩存更新操作
    CachePut[] put() default {};
    
    // 用於指定多個緩存失效操作
    CacheEvict[] evict() default {};
}

操練一下

@CacheConfig(cacheNames = {"emp"}, /*keyGenerator = "cacheKeyGenerator"*/)
public class EmployeeServiceImpl implements EmployeeService {
    @Override
    @Cacheable(/*value = ‘emp’*/ key = "targetClass + methodName +#p0")
    public Employee getEmp(Integer id){
        Employee emp = employeeMapper.getEmpById(id);
        return emp;
    }
}

8.主鍵生成策略 keyGenerator

操練一下

創建CacheConfig配置類

@Configuration
public class CacheConfig {

	/**
	 * 生成緩存主鍵策略 (方法名+參數)
	 * 
	 * @return KeyGenerator
	 */
	@Bean("cacheKeyGenerator")
	public KeyGenerator keyGenerator() {
		return (target, method, params) -> (method.getName() + " [ " + Arrays.asList(params) + " ]");
	}
}


可以在@CacheConfig指定生成策略,也可以在@Cacheable/@CachePut/@CacheEvict指定key生成策略

@CacheConfig(cacheNames = {"emp"}, keyGenerator = "cacheKeyGenerator")
public class EmployeeServiceImpl implements EmployeeService {
    @Override
    @Cacheable(/*value = ‘emp’,keyGenerator = "cacheKeyGenerator"*/)
    public Employee getEmp(Integer id){
        Employee emp = employeeMapper.getEmpById(id);
        return emp;
    }
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM