1. 程式人生 > >SpringBoot基礎系列-SpringCache使用

SpringBoot基礎系列-SpringCache使用

概述 onf false rri array uil 優先級 自動發現 原創文章

原創文章,轉載請標註出處:《SpringBoot基礎系列-SpringCache使用》

一、概述

SpringCache本身是一個緩存體系的抽象實現,並沒有具體的緩存能力,要使用SpringCache還需要配合具體的緩存實現來完成。

雖然如此,但是SpringCache是所有Spring支持的緩存結構的基礎,而且所有的緩存的使用最後都要歸結於SpringCache,那麽一來,要想使用SpringCache,還是要仔細研究一下的。

二、緩存註解

SpringCache緩存功能的實現是依靠下面的這幾個註解完成的。

  • @EnableCaching:開啟緩存功能
  • @Cacheable:定義緩存,用於觸發緩存
  • @CachePut:定義更新緩存,觸發緩存更新
  • @CacheEvict:定義清除緩存,觸發緩存清除
  • @Caching:組合定義多種緩存功能
  • @CacheConfig:定義公共設置,位於class之上

    2.1 @EnableCaching

    該註解主要用於開啟基於註解的緩存功能,使用方式為:
@EnableCaching
@Configuration
public class CacheConfig {
    @Bean
    public CacheManager cacheManager() {
        SimpleCacheManager cacheManager = new SimpleCacheManager();
        cacheManager.setCaches(Arrays.asList(new ConcurrentMapCache("default")));
        return cacheManager;
    }
}

註意:在SpringBoot中使用SpringCache可以由自動配置功能來完成CacheManager的註冊,SpringBoot會自動發現項目中擁有的緩存系統,而註冊對應的緩存管理器,當然我們也可以手動指定。

使用該註解和如下XML配置具有一樣的效果:

<beans>
    <cache:annotation-driven/>
    <bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager>
        <property name="caches">
            <set>
                <bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean>
                    <property name="name" value="default"/>
                </bean>
            </set>
        </property>
    </bean>
</beans>

下面來看看@EnableCaching的源碼:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(CachingConfigurationSelector.class)
public @interface EnableCaching {
    // 用於設置使用哪種代理方式,默認為基於接口的JDK動態代理(false),
    // 設置為true,則使用基於繼承的CGLIB動態代理
    boolean proxyTargetClass() default false;
    // 用於設置切面織入方式(設置面向切面編程的實現方式),
    // 默認為使用動態代理的方式織入,當然也可以設置為ASPECTJ的方式來實現AOP
    AdviceMode mode() default AdviceMode.PROXY;
    // 用於設置在一個切點存在多個通知的時候各個通知的執行順序,默認為最低優先級,
    // 其中數字卻大優先級越低,這裏默認為最低優先級,int LOWEST_PRECEDENCE =
    // Integer.MAX_VALUE;,卻是整數的最大值
    int order() default Ordered.LOWEST_PRECEDENCE;
}
public enum AdviceMode {
    PROXY,
    ASPECTJ
}
public interface Ordered {
    int HIGHEST_PRECEDENCE = Integer.MIN_VALUE;
    int LOWEST_PRECEDENCE = Integer.MAX_VALUE;
    int getOrder();
}

由上面的源碼可以看出,緩存功能是依靠AOP來實現的。

2.2 @Cacheable

該註解用於標註於方法之上用於標識該方法的返回結果需要被緩存起來,標註於類之上標識該類中所有方法均需要將結果緩存起來。

該註解標註的方法每次被調用前都會觸發緩存校驗,校驗指定參數的緩存是否已存在(已發生過相同參數的調用),若存在,直接返回緩存結果,否則執行方法內容,最後將方法執行結果保存到緩存中。

2.2.1 使用

@Service
@Log4j2
public class AnimalService {
    @Autowired
    private AnimalRepository animalRepository;
    //...
//    @Cacheable("animalById")
    @Cacheable(value = "animalById", key = "#id")
    public ResponseEntity<Animal> getAnimalById(final int id){
        return ResponseEntity.ok(animalRepository.selectById(id));
    }
    //...
}

上面的實例中兩個@Cacheable配置效果其實是一樣的,其中value指定的緩存的名稱,它和另一個方法cacheName效果一樣,一般來說這個緩存名稱必須要有,因為這個是區別於其他方法的緩存的唯一方法。

這裏我們介紹一下緩存的簡單結構,在緩存中,每個這樣的緩存名稱的名下都會存在著多個緩存條目,這些緩存條目對應在使用不同的參數調用當前方法時生成的緩存,所有一個緩存名稱並不是一個緩存,而是一系列緩存。

另一個key用於指定當前方法的緩存保存時的鍵的組合方式,默認的情況下使用所有的參數組合而成,這樣可以有效區分不同參數的緩存。當然我們也可以手動指定,指定的方法是使用SPEL表達式。

這裏我麽來簡單看看其源碼,了解下其他幾個方法的作用:

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Cacheable {
    // 用於指定緩存名稱,與cacheNames()方法效果一致
    @AliasFor("cacheNames")
    String[] value() default {};
    // 用於指定緩存名稱,與value()方法效果一致
    @AliasFor("value")
    String[] cacheNames() default {};
    // 用於使用SPEL手動指定緩存鍵的組合方式,默認情況使用所有的參數來組合成鍵,除非自定義了keyGenerator。
    // 使用SPEL表達式可以根據上下文環境來獲取到指定的數據:
    // #root.method:用於獲取當前方法的Method實例
    // #root.target:用於獲取當前方法的target實例
    // #root.caches:用於獲取當前方法關聯的緩存
    // #root.methodName:用於獲取當前方法的名稱
    // #root.targetClass:用於獲取目標類類型
    // #root.args[1]:獲取當前方法的第二個參數,等同於:#p1和#a1和#argumentName
    String key() default "";
    // 自定義鍵生成器,定義了該方法之後,上面的key方法自動失效,這個鍵生成器是:
    // org.springframework.cache.interceptor.KeyGenerator,這是一個函數式接口,
    // 只有一個generate方法,我們可以通過自定義的邏輯來實現自定義的key生成策略。
    String keyGenerator() default "";
    // 用於設置自定義的cacheManager(緩存管理器),可以自動生成一個cacheResolver
    // (緩存解析器),這一下面的cacheResolver()方法設置互斥
    String cacheManager() default "";
    // 用於設置一個自定義的緩存解析器
    String cacheResolver() default "";
    // 用於設置執行緩存的條件,如果條件不滿足,方法返回的結果就不會被緩存,默認無條件全部緩存。
    // 同樣使用SPEL來定義條件,可以使用的獲取方式同key方法。
    String condition() default "";
    // 這個用於禁止緩存功能,如果設置的條件滿足,就不執行緩存結果,與上面的condition不同之處在於,
    // 該方法執行在當前方法調用結束,結果出來之後,因此,它除了可以使用上面condition所能使用的SPEL
    // 表達式之外,還可以使用#result來獲取方法的執行結果,亦即可以根據結果的不同來決定是否緩存。
    String unless() default "";
    // 設置是否對多個針對同一key執行緩存加載的操作的線程進行同步,默認不同步。這個功能需要明確確定所
    // 使用的緩存工具支持該功能,否則不要濫用。
    boolean sync() default false;
}

如何自定義一個KeyGenerator呢?

public class AnimalKeyGenerator implements KeyGenerator {
    @Override
    public Object generate(Object target, Method method, Object... params) {
        StringBuilder sb = new StringBuilder("animal-");
        sb.append(target.getClass().getSimpleName()).append("-").append(method.getName()).append("-");
        for (Object o : params) {
            String s = o.toString();
            sb.append(s).append("-");
        }
        return sb.deleteCharAt(sb.lastIndexOf("-")).toString();
    }
}

2.3 @CachePut

該註解用於更新緩存,無論結果是否已經緩存,都會在方法執行結束插入緩存,相當於更新緩存。一般用於更新方法之上。

@Service
@Log4j2
public class AnimalService {
    @Autowired
    private AnimalRepository animalRepository;
    //...
    @CachePut(value = "animalById", key = "#animal.id")
    public ResponseEntity<Animal> updateAnimal(final Animal animal){
        Wrapper<Animal> animalWrapper = new UpdateWrapper<>();
        ((UpdateWrapper<Animal>) animalWrapper).eq("id",animal.getId());
        animalRepository.update(animal, animalWrapper);
        return ResponseEntity.ok(this.getAnimalById(animal.getId()));
    }
    //...
}

這裏指定更新緩存,value同樣還是緩存名稱,這裏更新的是上面查詢操作的同一緩存,而且key設置為id也與上面的key設置對應。

如此設置之後,每次執行update方法時都會直接執行方法內容,然後將返回的結果保存到緩存中,如果存在相同的key,直接替換緩存內容執行緩存更新。

下面來看看源碼:

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
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 "";
}

只有一點要註意:這裏的設置一定要和執行緩存保存的方法的@Cacheable的設置一致,否則無法準確更新。

2.4 @CacheEvict

該註解主要用於刪除緩存操作。

@Service
@Log4j2
public class AnimalService {
    @Autowired
    private AnimalRepository animalRepository;
    //...
    @CacheEvict(value = "animalById", key = "#id")
    public ResponseEntity<Integer> deleteAnimalById(final int id){
        return ResponseEntity.ok(animalRepository.deleteById(id));
    }
    //...
}

簡單明了,看看源碼:

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
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 "";
    // 這個設置用於指定當前緩存名稱名下的所有緩存是否全部刪除,默認false。
    boolean allEntries() default false;
    // 這個用於指定刪除緩存的操作是否在方法調用之前完成,默認為false,表示先調用方法,在執行緩存刪除。
    boolean beforeInvocation() default false;
}

2.5 @Caching

這個註解用於組個多個緩存操作,包括針對不用緩存名稱的相同操作等,源碼:

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Caching {
    // 用於指定多個緩存設置操作
    Cacheable[] cacheable() default {};
    // 用於指定多個緩存更新操作
    CachePut[] put() default {};
    // 用於指定多個緩存失效操作
    CacheEvict[] evict() default {};
}

簡單用法:

@Service
@Log4j2
public class AnimalService {
    @Autowired
    private AnimalRepository animalRepository;
    //...
    @Caching(
        evict = {
            @CacheEvict(value = "animalById", key = "#id"),
            @CacheEvict(value = "animals", allEntries = true, beforeInvocation = true)
        }
    )
    public ResponseEntity<Integer> deleteAnimalById(final int id){
        return ResponseEntity.ok(animalRepository.deleteById(id));
    }
    @Cacheable("animals")
    public ResponseEntity<Page<Animal>> getAnimalPage(final Animal animal, final int pageId, final int pageSize){
        Page<Animal> page = new Page<>();
        page.setCurrent(pageId);
        page.setSize(pageSize);
        return ResponseEntity.ok((Page<Animal>) animalRepository.selectPage(page,packWrapper(animal, WrapperType.QUERY)));
    }
    //...
}

2.6 @CacheConfig

該註解標註於類之上,用於進行一些公共的緩存相關配置。源碼為:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CacheConfig {
    // 設置統一的緩存名,適用於整個類中的方法全部是針對同一緩存名操作的情況
    String[] cacheNames() default {};
    // 設置統一個鍵生成器,免去了每個緩存設置中單獨設置
    String keyGenerator() default "";
    // 設置統一個自定義緩存管理器
    String cacheManager() default "";
    // 設置統一個自定義緩存解析器
    String cacheResolver() default "";
}

SpringBoot基礎系列-SpringCache使用