Spring使用Cache

3.1開始,Spring引入了對Cache的支持。其使用方法和原理都相似於Spring對事務管理的支持。Spring Cache是做用在方法上的,其核心思想是這樣的:當咱們在調用一個緩存方法時會把該方法參數和返回結果做爲一個鍵值對存放在緩存中,等到下次利用一樣的參數來調用該方法時將再也不執行該方法,而是直接從緩存中獲取結果進行返回。因此在使用Spring Cache的時候咱們要保證咱們緩存的方法對於相同的方法參數要有相同的返回結果。 spring

       使用Spring Cache須要咱們作兩方面的事: 數組

n  聲明某些方法使用緩存 緩存

n  配置SpringCache的支持 spa

       Spring對事務管理的支持同樣,SpringCache的支持也有基於註解和基於XML配置兩種方式。下面咱們先來看看基於註解的方式。 代理

 

1       基於註解的支持

       Spring爲咱們提供了幾個註解來支持Spring Cache。其核心主要是@Cacheable@CacheEvict。使用@Cacheable標記的方法在執行後Spring Cache將緩存其返回結果,而使用@CacheEvict標記的方法會在方法執行前或者執行後移除Spring Cache中的某些元素。下面咱們未來詳細介紹一下Spring基於註解對Cache的支持所提供的幾個註解。 orm

1.1    @Cacheable

       @Cacheable能夠標記在一個方法上,也能夠標記在一個類上。當標記在一個方法上時表示該方法是支持緩存的,當標記在一個類上時則表示該類全部的方法都是支持緩存的。對於一個支持緩存的方法,Spring會在其被調用後將其返回值緩存起來,以保證下次利用一樣的參數來執行該方法時能夠直接從緩存中獲取結果,而不須要再次執行該方法。Spring在緩存方法的返回值時是以鍵值對進行緩存的,值就是方法的返回結果,至於鍵的話,Spring又支持兩種策略,默認策略和自定義策略,這個稍後會進行說明。須要注意的是當一個支持緩存的方法在對象內部被調用時是不會觸發緩存功能的。@Cacheable能夠指定三個屬性,valuekeycondition xml

1.1.1  value屬性指定Cache名稱

       value屬性是必須指定的,其表示當前方法的返回值是會被緩存在哪一個Cache上的,對應Cache的名稱。其能夠是一個Cache也能夠是多個Cache,當須要指定多個Cache時其是一個數組。 對象

   @Cacheable("cache1")//Cache是發生在cache1上的 接口

   public User find(Integer id) { 事務

      returnnull;

   }

 

   @Cacheable({"cache1""cache2"})//Cache是發生在cache1cache2上的

   public User find(Integer id) {

      returnnull;

   }

 

1.1.2  使用key屬性自定義key

       key屬性是用來指定Spring緩存方法的返回結果時對應的key的。該屬性支持SpringEL表達式。當咱們沒有指定該屬性時,Spring將使用默認策略生成key。咱們這裏先來看看自定義策略,至於默認策略會在後文單獨介紹。

       自定義策略是指咱們能夠經過SpringEL表達式來指定咱們的key。這裏的EL表達式可使用方法參數及它們對應的屬性。使用方法參數時咱們能夠直接使用「#參數名」或者「#p參數index」。下面是幾個使用參數做爲key的示例。

   @Cacheable(value="users", key="#id")

   public User find(Integer id) {

      returnnull;

   }

 

   @Cacheable(value="users", key="#p0")

   public User find(Integer id) {

      returnnull;

   }

 

   @Cacheable(value="users", key="#user.id")

   public User find(User user) {

      returnnull;

   }

 

   @Cacheable(value="users", key="#p0.id")

   public User find(User user) {

      returnnull;

   }

 

       除了上述使用方法參數做爲key以外,Spring還爲咱們提供了一個root對象能夠用來生成key。經過該root對象咱們能夠獲取到如下信息。

屬性名稱

描述

示例

methodName

當前方法名

#root.methodName

method

當前方法

#root.method.name

target

當前被調用的對象

#root.target

targetClass

當前被調用的對象的class

#root.targetClass

args

當前方法參數組成的數組

#root.args[0]

caches

當前被調用的方法使用的Cache

#root.caches[0].name

 

       當咱們要使用root對象的屬性做爲key時咱們也能夠將「#root」省略,由於Spring默認使用的就是root對象的屬性。如:

   @Cacheable(value={"users""xxx"}, key="caches[1].name")

   public User find(User user) {

      returnnull;

   }

 

1.1.3  condition屬性指定發生的條件

       有的時候咱們可能並不但願緩存一個方法全部的返回結果。經過condition屬性能夠實現這一功能。condition屬性默認爲空,表示將緩存全部的調用情形。其值是經過SpringEL表達式來指定的,當爲true時表示進行緩存處理;當爲false時表示不進行緩存處理,即每次調用該方法時該方法都會執行一次。以下示例表示只有當userid爲偶數時纔會進行緩存。

   @Cacheable(value={"users"}, key="#user.id", condition="#user.id%2==0")

   public User find(User user) {

      System.out.println("find user by user " + user);

      return user;

   }

 

1.2     @CachePut

       在支持Spring Cache的環境下,對於使用@Cacheable標註的方法,Spring在每次執行前都會檢查Cache中是否存在相同key的緩存元素,若是存在就再也不執行該方法,而是直接從緩存中獲取結果進行返回,不然纔會執行並將返回結果存入指定的緩存中。@CachePut也能夠聲明一個方法支持緩存功能。與@Cacheable不一樣的是使用@CachePut標註的方法在執行前不會去檢查緩存中是否存在以前執行過的結果,而是每次都會執行該方法,並將執行結果以鍵值對的形式存入指定的緩存中。

       @CachePut也能夠標註在類上和方法上。使用@CachePut時咱們能夠指定的屬性跟@Cacheable是同樣的。

   @CachePut("users")//每次都會執行方法,並將結果存入指定的緩存中

   public User find(Integer id) {

      returnnull;

   }

 

1.3     @CacheEvict

       @CacheEvict是用來標註在須要清除緩存元素的方法或類上的。當標記在一個類上時表示其中全部的方法的執行都會觸發緩存的清除操做。@CacheEvict能夠指定的屬性有valuekeyconditionallEntriesbeforeInvocation。其中valuekeycondition的語義與@Cacheable對應的屬性相似。即value表示清除操做是發生在哪些Cache上的(對應Cache的名稱);key表示須要清除的是哪一個key,如未指定則會使用默認策略生成的keycondition表示清除操做發生的條件。下面咱們來介紹一下新出現的兩個屬性allEntriesbeforeInvocation

1.3.1  allEntries屬性

       allEntriesboolean類型,表示是否須要清除緩存中的全部元素。默認爲false,表示不須要。當指定了allEntriestrue時,Spring Cache將忽略指定的key。有的時候咱們須要Cache一下清除全部的元素,這比一個一個清除元素更有效率。

   @CacheEvict(value="users", allEntries=true)

   public void delete(Integer id) {

      System.out.println("delete user by id: " + id);

   }

 

1.3.2  beforeInvocation屬性

       清除操做默認是在對應方法成功執行以後觸發的,即方法若是由於拋出異常而未能成功返回時也不會觸發清除操做。使用beforeInvocation能夠改變觸發清除操做的時間,當咱們指定該屬性值爲true時,Spring會在調用該方法以前清除緩存中的指定元素。

   @CacheEvict(value="users", beforeInvocation=true)

   public void delete(Integer id) {

      System.out.println("delete user by id: " + id);

   }

 

       其實除了使用@CacheEvict清除緩存元素外,當咱們使用Ehcache做爲實現時,咱們也能夠配置Ehcache自身的驅除策略,其是經過Ehcache的配置文件來指定的。因爲Ehcache不是本文描述的重點,這裏就很少贅述了,想了解更多關於Ehcache的信息,請查看我關於Ehcache的專欄

 

1.4     @Caching

       @Caching註解可讓咱們在一個方法或者類上同時指定多個Spring Cache相關的註解。其擁有三個屬性:cacheableputevict,分別用於指定@Cacheable@CachePut@CacheEvict

   @Caching(cacheable = @Cacheable("users"), evict = { @CacheEvict("cache2"),

         @CacheEvict(value = "cache3", allEntries = true) })

   public User find(Integer id) {

      returnnull;

   }

 

1.5     使用自定義註解

       Spring容許咱們在配置可緩存的方法時使用自定義的註解,前提是自定義的註解上必須使用對應的註解進行標註。如咱們有以下這麼一個使用@Cacheable進行標註的自定義註解。

@Target({ElementType.TYPE, ElementType.METHOD})

@Retention(RetentionPolicy.RUNTIME)

@Cacheable(value="users")

public @interface MyCacheable {

 

}

       那麼在咱們須要緩存的方法上使用@MyCacheable進行標註也能夠達到一樣的效果。

   @MyCacheable

   public User findById(Integer id) {

      System.out.println("find user by id: " + id);

      User user = new User();

      user.setId(id);

      user.setName("Name" + id);

      return user;

   }

 

2       配置SpringCache的支持

2.1     聲明對Cache的支持

2.1.1  基於註解

       配置Spring對基於註解的Cache的支持,首先咱們須要在Spring的配置文件中引入cache命名空間,其次經過<cache:annotation-driven />就能夠啓用Spring對基於註解的Cache的支持。

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"

   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

   xmlns:cache="http://www.springframework.org/schema/cache"

   xsi:schemaLocation="http://www.springframework.org/schema/beans

     http://www.springframework.org/schema/beans/spring-beans-3.0.xsd

     http://www.springframework.org/schema/cache

     http://www.springframework.org/schema/cache/spring-cache.xsd">

 

   <cache:annotation-driven/>

 

</beans>

 

       <cache:annotation-driven/>有一個cache-manager屬性用來指定當前所使用的CacheManager對應的bean的名稱,默認是cacheManager,因此當咱們的CacheManageridcacheManager時咱們能夠不指定該參數,不然就須要咱們指定了。

       <cache:annotation-driven/>還能夠指定一個mode屬性,可選值有proxyaspectj。默認是使用proxymodeproxy時,只有緩存方法在外部被調用的時候Spring Cache纔會發生做用,這也就意味着若是一個緩存方法在其聲明對象內部被調用時Spring Cache是不會發生做用的。而modeaspectj時就不會有這種問題。另外使用proxy時,只有public方法上的@Cacheable等標註纔會起做用,若是須要非public方法上的方法也可使用Spring Cache時把mode設置爲aspectj

       此外,<cache:annotation-driven/>還能夠指定一個proxy-target-class屬性,表示是否要代理class,默認爲false。咱們前面提到的@Cacheable@cacheEvict等也能夠標註在接口上,這對於基於接口的代理來講是沒有什麼問題的,可是須要注意的是當咱們設置proxy-target-classtrue或者modeaspectj時,是直接基於class進行操做的,定義在接口上的@CacheableCache註解不會被識別到,那對應的Spring Cache也不會起做用了。

       須要注意的是<cache:annotation-driven/>只會去尋找定義在同一個ApplicationContext下的@Cacheable等緩存註解。

 

2.1.2  基於XML配置

       除了使用註解來聲明對Cache的支持外,Spring還支持使用XML來聲明對Cache的支持。這主要是經過相似於aop:advicecache:advice來進行的。在cache命名空間下定義了一個cache:advice元素用來定義一個對於Cacheadvice。其須要指定一個cache-manager屬性,默認爲cacheManagercache:advice下面能夠指定多個cache:caching元素,其有點相似於使用註解時的@Caching註解。cache:caching元素下又能夠指定cache:cacheablecache:cache-putcache:cache-evict元素,它們相似於使用註解時的@Cacheable@CachePut@CacheEvict。下面來看一個示例:

   <cache:advice id="cacheAdvice" cache-manager="cacheManager">

      <cache:caching cache="users">

         <cache:cacheable method="findById" key="#p0"/>

         <cache:cacheable method="find" key="#user.id"/>

         <cache:cache-evict method="deleteAll" all-entries="true"/>

      </cache:caching>

   </cache:advice>

 

       上面配置定義了一個名爲cacheAdvicecache:advice,其中指定了將緩存findById方法和find方法到名爲users的緩存中。這裏的方法還可使用通配符「*」,好比「find*」表示任何以「find」開始的方法。

       有了cache:advice以後,咱們還須要引入aop命名空間,而後經過aop:config指定定義好的cacheAdvice要應用在哪些pointcut上。如:

   <aop:config proxy-target-class="false">

      <aop:advisor advice-ref="cacheAdvice" pointcut="execution(* com.xxx.UserService.*(..))"/>

   </aop:config>

       上面的配置表示在調用com.xxx.UserService中任意公共方法時將使用cacheAdvice對應的cache:advice來進行Spring Cache處理。更多關於Spring Aop的內容不在本文討論範疇內。

 

2.2     配置CacheManager

       CacheManagerSpring定義的一個用來管理Cache的接口。Spring自身已經爲咱們提供了兩種CacheManager的實現,一種是基於Java APIConcurrentMap,另外一種是基於第三方Cache實現——Ehcache,若是咱們須要使用其它類型的緩存時,咱們能夠本身來實現SpringCacheManager接口或AbstractCacheManager抽象類。下面分別來看看Spring已經爲咱們實現好了的兩種CacheManager的配置示例。

2.2.1  基於ConcurrentMap的配置

   <bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager">

      <property name="caches">

         <set>

            <beanclass="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean" p:name="xxx"/>

         </set>

      </property>

   </bean>

       上面的配置使用的是一個SimpleCacheManager,其中包含一個名爲「xxx」的ConcurrentMapCache

 

2.2.2  基於Ehcache的配置

   <!-- Ehcache實現 -->

   <bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager"p:cache-manager-ref="ehcacheManager"/>

   <bean id="ehcacheManager"class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean" p:config-location="ehcache-spring.xml"/>

       上面的配置使用了一個Spring提供的EhCacheCacheManager來生成一個Spring的CacheManager,其接收一個Ehcache的CacheManager,由於真正用來存入緩存數據的仍是Ehcache。Ehcache的CacheManager是經過Spring提供的EhCacheManagerFactoryBean來生成的,其能夠經過指定ehcache的配置文件位置來生成一個Ehcache的CacheManager。若未指定則將按照Ehcache的默認規則取classpath根路徑下的ehcache.xml文件,若該文件也不存在,則獲取Ehcache對應jar包中的ehcache-failsafe.xml文件做爲配置文件。更多關於Ehcache的內容這裏就很少說了,它不屬於本文討論的內容,欲瞭解更多關於Ehcache的內容能夠參考我以前發佈的Ehcache系列文章,也能夠參考官方文檔等。

 

3       鍵的生成策略

       鍵的生成策略有兩種,一種是默認策略,一種是自定義策略。

3.1     默認策略

       默認的key生成策略是經過KeyGenerator生成的,其默認策略以下:

n  若是方法沒有參數,則使用0做爲key

n  若是隻有一個參數的話則使用該參數做爲key

n  若是參數多餘一個的話則使用全部參數的hashCode做爲key

 

       若是咱們須要指定本身的默認策略的話,那麼咱們能夠實現本身的KeyGenerator,而後指定咱們的Spring Cache使用的KeyGenerator爲咱們本身定義的KeyGenerator

       使用基於註解的配置時是經過cache:annotation-driven指定的.

   <cache:annotation-driven key-generator="userKeyGenerator"/>

  

   <bean id="userKeyGenerator" class="com.xxx.cache.UserKeyGenerator"/>

 

       而使用基於XML配置時是經過cache:advice來指定的。

   <cache:advice id="cacheAdvice" cache-manager="cacheManager" key-generator="userKeyGenerator">

   </cache:advice>

 

       須要注意的是此時咱們全部的Cache使用的Key的默認生成策略都是同一個KeyGenerator

3.2     自定義策略

       自定義策略是指咱們能夠經過SpringEL表達式來指定咱們的key。這裏的EL表達式可使用方法參數及它們對應的屬性。使用方法參數時咱們能夠直接使用「#參數名」或者「#p參數index」。下面是幾個使用參數做爲key的示例。

   @Cacheable(value="users", key="#id")

   public User find(Integer id) {

      returnnull;

   }

 

   @Cacheable(value="users", key="#p0")

   public User find(Integer id) {

      returnnull;

   }

 

   @Cacheable(value="users", key="#user.id")

   public User find(User user) {

      returnnull;

   }

 

   @Cacheable(value="users", key="#p0.id")

   public User find(User user) {

      returnnull;

   }

 

       除了上述使用方法參數做爲key以外,Spring還爲咱們提供了一個root對象能夠用來生成key。經過該root對象咱們能夠獲取到如下信息。

屬性名稱

描述

示例

methodName

當前方法名

#root.methodName

method

當前方法

#root.method.name

target

當前被調用的對象

#root.target

targetClass

當前被調用的對象的class

#root.targetClass

args

當前方法參數組成的數組

#root.args[0]

caches

當前被調用的方法使用的Cache

#root.caches[0].name

 

       當咱們要使用root對象的屬性做爲key時咱們也能夠將「#root」省略,由於Spring默認使用的就是root對象的屬性。如:

   @Cacheable(value={"users""xxx"}, key="caches[1].name")

   public User find(User user) {

      returnnull;

   }

 

4       Spring單獨使用Ehcache

       前面介紹的內容是Spring內置的對Cache的支持,其實咱們也能夠經過Spring本身單獨的使用EhcacheCacheManagerEhcache對象。經過在Application Context中配置EhCacheManagerFactoryBeanEhCacheFactoryBean,咱們就能夠把對應的EhCacheCacheManagerEhcache對象注入到其它的Spring bean對象中進行使用。

 

4.1     EhCacheManagerFactoryBean

     EhCacheManagerFactoryBeanSpring內置的一個能夠產生EhcacheCacheManager對象的FactoryBean。其能夠經過屬性configLocation指定用於建立CacheManagerEhcache配置文件的路徑,一般是ehcache.xml文件的路徑。若是沒有指定configLocation,則將使用默認位置的配置文件建立CacheManager,這是屬於Ehcache自身的邏輯,即若是在classpath根路徑下存在ehcache.xml文件,則直接使用該文件做爲Ehcache的配置文件,不然將使用ehcache-xxx.jar中的ehcache-failsafe.xml文件做爲配置文件來建立EhcacheCacheManager。此外,若是不但願建立的CacheManager使用默認的名稱(在ehcache.xml文件中定義的,或者是由CacheManager內部定義的),則能夠經過cacheManagerName屬性進行指定。下面是一個配置EhCacheManagerFactoryBean的示例。

   <!-- 定義CacheManager -->

   <bean id="cacheManager"class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">

      <!-- 指定配置文件的位置 -->

      <property name="configLocation" value="/WEB-INF/config/ehcache.xml"/>

      <!-- 指定新建的CacheManager的名稱 -->

      <property name="cacheManagerName" value="cacheManagerName"/>

   </bean>

 

4.2     EhCacheFactoryBean

       EhCacheFactoryBean是用來產生EhcacheEhcache對象的FactoryBean。定義EhcacheFactoryBean時有兩個很重要的屬性咱們能夠來指定。一個是cacheManager屬性,其能夠指定將用來獲取或建立EhcacheCacheManager對象,若未指定則將經過CacheManager.create()獲取或建立默認的CacheManager。另外一個重要屬性是cacheName,其表示當前EhCacheFactoryBean對應的是CacheManager中的哪個Ehcache對象,若未指定默認使用beanName做爲cacheName。若CacheManager中不存在對應cacheNameEhcache對象,則將使用CacheManager建立一個名爲cacheNameCache對象。此外咱們還能夠經過EhCacheFactoryBeantimeToIdletimeToLive等屬性指定要建立的Cache的對應屬性,注意這些屬性只對CacheManager中不存在對應Cache時新建的Cache才起做用,對已經存在的Cache將不起做用,更多屬性設置請參考SpringAPI文檔。此外還有幾個屬性是對不論是已經存在仍是新建立的Cache都起做用的屬性:statisticsEnabledsampledStatisticsEnableddisabledblockingcacheEventListeners,其中前四個默認都是false,最後一個表示爲當前Cache指定CacheEventListener。下面是一個定義EhCacheFactoryBean的示例。

   <!-- 定義CacheManager -->

   <bean id="cacheManager"class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">

      <!-- 指定配置文件的位置 -->

      <property name="configLocation" value="/WEB-INF/config/ehcache.xml"/>

      <!-- 指定新建的CacheManager的名稱 -->

      <property name="cacheManagerName" value="cacheManagerName"/>

   </bean>

  

   <!-- 定義一個Ehcache -->

   <bean id="userCache" class="org.springframework.cache.ehcache.EhCacheFactoryBean">

      <property name="cacheName" value="user"/>

      <property name="cacheManager" ref="cacheManager"/>

   </bean>

 

(注:本文是基於Spring3.1.0所寫)

相關文章
相關標籤/搜索