Spring 使用Cache

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

使用Spring Cache需要我們做兩方面的事:

l  聲明某些方法使用緩存

l  配置Spring對Cache的支持

和Spring對事物管理的支持一樣,Spring對Cache的支持也有基於註解和基於XML配置兩種方式。下面我們先來看看基於註解的方式。

第一章 基於註解的支持

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

1.1 @Cacheable

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

1.1.1 value屬性指定Cache的名稱

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

//Cache是發生在Cache1上的

@Cacheable("Cache1"

   public User get(Long id) {

      returnnull;

   }

//Cache是發生在Cache1Cache2上的

@Cacheable({"Cache1","Cache2"}

   public User get(Long id) {

      returnnull;

   }

 

1.1.2 使用key屬性自定義key(鍵的生成策略--自定義策略)

key屬性是用來指定Spring緩存方法的返回結果時對應的key的。該屬性支持Spring EL表達式。當我們沒有指定該屬性時,Spring將使用默認策略生成key(第三章的鍵生成策略講到)。我們先來看看自定義策略。

自定義策略是指我們可以通過Spring的EL表達式來指定我們的key。這裏的EL表達式可以使用方法參數及他們對應的屬性,使用方法參數時我們可以直接使用“#參數名”或者“p參數index”。下面時幾個使用參數作爲key的示例。

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

   public User get(Long id) {

      return null;

   }

 

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

   public User get(Long id) {

      return null;

   }

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

   public User get(User user) {

      return null;

   }

 

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

   public User get(User user) {

      return null;

   }

 

除了使用方法參數作爲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={"userCache","xxx"},key="caches[1].name")

   public User get(User user) {

      return null;

   }

1.1.3 Condition屬性指定發生的條件

當我們不希望緩存一個方法所有的返回結果時,通過condition屬性可以實現這一功能。condition屬性默認爲空,表示將緩存所有的調用情形。其值是通過Spring EL表達式來指定的,當爲true時表示進行緩存處理;當爲false時表示不進行緩存處理,即每次調用該方法都會執行一次。如下示例表示只有當user的id爲偶數時才進行緩存。

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

   public User get(User user) {

System.out.println("select user from DB.");

      returnuserDAO.getAll();

   }

1.2 @CachePut

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

@CachePut也可以標註在類上和方法上。使用@CachePut時我們可以指定的屬性跟@Cacheable是一樣的。

//每次都會執行方法,並將返回結果存放在指定的緩存中

@CachePut("userCache")

   public User get(Long id) {

      return null;

   }

1.3 @CacheEvict

@CacheEvict是用來標註在需要清除緩存元素的方法或類上的。當標記在一個類上時表示其中所有的方法的執行都會觸發緩存的清除操作。@CacheEvict可以指定的屬性有value、key、condition、allEntries和beforeInvocation。其中value、key和condition的語義與@Cacheable對應的屬性類似。即value表示清除操作是發生在哪些Cache上的(對應Cache的名稱);key表示需要清除的是哪個key,如未指定則會使用默認策略生成的key;condition表示清除操作發生的條件。下面來介紹下屬性allEntries和beforeInvocation。

1.3.1 allEntries屬性(調用後清除緩存,默認false)

allEntries是boolean類型,表示是否需要清除緩存中的所有元素。默認爲false,表示不需要。當指定了allEntries爲true時,Spring Cache將忽略指定的key。有的時候我們需要Cache一下清除所有的元素,這比一個一個清除元素更爲有效。

根據id清除:

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

   public void delete(Long id) {

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

   }

1.3.2 beforeInvocation屬性(先清空緩存,再進行查詢)

清除操作默認是在對應方法成功執行之後觸發的,即方法如果因爲異常而未能成功返回時也不會觸發清除操作。使用beforeInvocation可以改變觸發清除操作的時間,當我們指定該屬性值爲true時,Spring會在調用該方法之前清除緩存中的元素。

根據id清除:

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

   public void delete(Long id) {

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

   }

其實除了使用@CacheEvict清除緩存元素外,當我們使用Ehcache作爲實現時,我們也可以配置Ehcache自身的驅除策略,其是通過Ehcache的配置文件來指定的。

1.4 @Caching

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

 

 

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

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

 

   public User get(Long id) {

      return null;

   }

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;

   }

第二章 配置Spring對Cache的支持

2.1 聲明對Cache的支持

2.1.1 基於註解

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

<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/>還可以指定一個mode屬性,可選值有proxy和aspectj。默認是使用proxy。當mode爲proxy時,只有緩存方法在外部被調用的時候Spring Cache纔會發生作用,這也就意味着如果一個緩存方法在其聲明對象內部被調用時Spring Cache是不會發生作用的。而mode爲aspectj時就不會有這種問題。另外使用proxy時,只有public方法上的@Cacheable等標註纔會起作用,如果需要非public方法上的方法也可以使用Spring Cache時把mode設置爲aspectj。

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

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

2.1.2 基於XML配置

除了使用註解來聲明對Cache的支持外,Spring還支持使用XML來聲明對Cache的支持。這主要是通過類似於aop:advice的cache:advice來進行的。在cache命名空間下定義了一個cache:advice元素用來定義一個對於Cache的advice。其需要指定一個cache-manager屬性,默認爲cacheManager。cache:advice下面可以指定多個cache:caching元素,其有點類似於使用註解時的@Caching註解。cache:caching元素下又可以指定cache:cacheable、cache:cache-put和cache: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>

上面配置定義了一個名爲cacheAdvice的cache: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處理。

2.2 配置CacheManager

CacheManager是Spring定義的一個用來管理Cache的接口。Spring自身已經爲我們提供了兩種CacheManager的實現,一種是基於Java API的ConcurrentMap,另一種是基於第三方Cache實現——Ehcache,如果我們需要使用其它類型的緩存時,我們可以自己來實現Spring的CacheManager接口或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對基於註解的Cache的支持 -->

   <cache:annotation-driven cache-manager ="cacheManager"/>

   <!-- cacheManager, 指定ehcache.xml的位置 -->

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

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

      <property name="configLocation"value="classpath:/ehcache.xml" />

    </bean>

上面的配置使用了一個Spring提供的EhCacheCacheManager來生成一個Spring的CacheManager,其接收一個Ehcache的CacheManager,因爲真正用來存入緩存數據的還是Ehcache。Ehcache的CacheManager是通過Spring提供的EhCacheManagerFactoryBean來生成的,其可以通過指定ehcache的配置文件位置來生成一個Ehcache的CacheManager。若未指定則將按照Ehcache的默認規則取classpath根路徑下的ehcache.xml文件,若該文件也不存在,則獲取Ehcache對應jar包中的ehcache-failsafe.xml文件作爲配置文件。

第三章 鍵的生成策略

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

3.1 默認策略

默認的key生成策略是通過KeyGenerator生成的,其默認策略如下:

l  如果方法沒有參數,則使用0作爲key。

l  如果只有一個參數的話則使用該參數作爲key。

l  如果參數多餘一個的話則使用所有參數的hashCode作爲key。

如果我們需要指定自己的默認策略的話,那麼我們可以實現自己的KeyGenerator,然後指定我們的Spring Cache使用的KeyGenerator爲我們自己定義的KeyGenerator。

 

1、使用基於註解的配置時是通過cache:annotation-driven指定的。

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

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

 

2、使用基於XML配置時是通過cache:advice來指定的。

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

</cache:advice>

需要注意的是此時我們所有的Cache使用的Key的默認生成策略都是同一個KeyGenerator。

3.2 自定義策略

(第一章中已介紹)

第四章 Spring單獨使用Ehcache

前面介紹的內容是Spring內置的對Cache的支持,其實我們也可以通過Spring自己單獨的使用Ehcache的CacheManager或Ehcache對象。通過在Application Context中配置EhCacheManagerFactoryBean和EhCacheFactoryBean,我們就可以把對應的EhCache的CacheManager和Ehcache對象注入到其它的Spring bean對象中進行使用。

4.1 EhCacheManagerFactoryBean

EhCacheManagerFactoryBean是Spring內置的一個可以產生Ehcache的CacheManager對象的FactoryBean。其可以通過屬性configLocation指定用於創建CacheManager的Ehcache配置文件的路徑,通常是ehcache.xml文件的路徑。如果沒有指定configLocation,則將使用默認位置的配置文件創建CacheManager,這是屬於Ehcache自身的邏輯,即如果在classpath根路徑下存在ehcache.xml文件,則直接使用該文件作爲Ehcache的配置文件,否則將使用ehcache-xxx.jar中的ehcache-failsafe.xml文件作爲配置文件來創建Ehcache的CacheManager。此外,如果不希望創建的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是用來產生Ehcache的Ehcache對象的FactoryBean。定義EhcacheFactoryBean時有兩個很重要的屬性我們可以來指定。一個是cacheManager屬性,其可以指定將用來獲取或創建Ehcache的CacheManager對象,若未指定則將通過CacheManager.create()獲取或創建默認的CacheManager。另一個重要屬性是cacheName,其表示當前EhCacheFactoryBean對應的是CacheManager中的哪一個Ehcache對象,若未指定默認使用beanName作爲cacheName。若CacheManager中不存在對應cacheName的Ehcache對象,則將使用CacheManager創建一個名爲cacheName的Cache對象。此外我們還可以通過EhCacheFactoryBean的timeToIdle、timeToLive等屬性指定要創建的Cache的對應屬性,注意這些屬性只對CacheManager中不存在對應Cache時新建的Cache才起作用,對已經存在的Cache將不起作用,更多屬性設置請參考Spring的API文檔。此外還有幾個屬性是對不管是已經存在還是新創建的Cache都起作用的屬性:statisticsEnabled、sampledStatisticsEnabled、disabled、blocking和cacheEventListeners,其中前四個默認都是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>

 

第五章 ehcache配置

5.1 Cache配置

 

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

 

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

         xsi:noNamespaceSchemaLocation="ehcache.xsd"

         updateCheck="true"monitoring="autodetect"

         dynamicConfig="true">

 

    <diskStore path="java.io.tmpdir"/>

 

    <transactionManagerLookup class="net.sf.ehcache.transaction.manager.DefaultTransactionManagerLookup"

                              properties="jndiName=java:/TransactionManager" propertySeparator=";"/>

 

    <cacheManagerEventListenerFactory class=""properties=""/>

 

    <cacheManagerPeerProviderFactory

            class="net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory"

            properties="peerDiscovery=automatic,

                       multicastGroupAddress=230.0.0.1,

                       multicastGroupPort=4446, timeToLive=1"

            propertySeparator=","

            />

 

    <cacheManagerPeerListenerFactory

            class="net.sf.ehcache.distribution.RMICacheManagerPeerListenerFactory"/>

 

    <defaultCache

            eternal="false"

            maxEntriesLocalHeap="10000"

            maxEntriesLocalDisk="1000"

            maxElementsOnDisk="10000000"

            maxElementsInMemory="3000"

            timeToIdleSeconds="120"

            timeToLiveSeconds="120"

            diskSpoolBufferSizeMB="30"

            diskExpiryThreadIntervalSeconds="120"

            memoryStoreEvictionPolicy="LRU">

        <persistence strategy="localTempSwap"/>

    </defaultCache>

 

    <cache name="userCache"

           eternal="false"

           maxEntriesLocalHeap="10000"

           maxEntriesLocalDisk="1000"

           maxElementsOnDisk="10000000"

           maxElementsInMemory="3000"

           diskSpoolBufferSizeMB="30"

           timeToIdleSeconds="300"

           timeToLiveSeconds="600"

           memoryStoreEvictionPolicy="LFU"

           diskExpiryThreadIntervalSeconds="120"

           transactionalMode="off">

        <persistence strategy="localTempSwap"/>

</cache>

</ehcache>

 

 

l  name:Cache的唯一標識名稱.

l  maxEntriesLocalHeap:堆內存中最大緩存對象數,0沒有限制

l  maxElementsInMemory:緩存最大個數。

l  eternal:對象是否永久有效,一但設置了,timeout將不起作用。

l  timeToIdleSeconds:設置對象在失效前的允許閒置時間(單位:秒)。僅當eternal=false對象不是永久有效時使用,可選屬性,默認值是0,也就是可閒置時間無窮大。

l  timeToLiveSeconds:設置對象在失效前允許存活時間(單位:秒)。最大時間介於創建時間和失效時間之間。僅當eternal=false對象不是永久有效時使用,默認是0.,也就是對象存活時間無窮大。 

l  overflowToDisk:當內存中對象數量達到maxElementsInMemory時,Ehcache將會對象寫到磁盤中。  

l  diskSpoolBufferSizeMB:這個參數設置DiskStore(磁盤緩存)的緩存區大小。默認是30MB。每個Cache都應該有自己的一個緩衝區。

l  maxElementsOnDisk:硬盤最大緩存個數。 

l  diskPersistent:是否緩存虛擬機重啓期數據 Whether the disk store persists between restarts of the Virtual Machine. The default value is false.  

l  diskExpiryThreadIntervalSeconds:磁盤失效線程運行時間間隔,默認是120秒。  

l  memoryStoreEvictionPolicy:當達到maxElementsInMemory限制時,Ehcache將會根據指定的策略去清理內存。默認策略是LRU(最近最少使用)。你可以設置爲FIFO(先進先出)或是LFU(較少使用)。  

l  clearOnFlush:內存數量最大時是否清除。

發佈了24 篇原創文章 · 獲贊 9 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章