天天看點

Spring Boot使用ehcache

最近看看了開源架構的 bootdo 源碼位置

就一直在糾結,我們使用Redis與ehcache的界限在哪裡。

我們都知道Redis的強,但是其實最強的那部分恰恰是我們很多小項目都用不上的。有時我們用來做session共享,有時我們用來做消息隊列,還有牛叉的哨兵機制等。我們普通使用時其實用不上這些牛叉的技術,又對Redis需要手動的put進去,get得到,有時我們其他的技術來代替。比如說Ehcache用注解的方式使用緩存。

一.spring cache

Spring Cache是作用在方法上的,其核心思想是這樣的:當我們在調用一個緩存方法時會把該方法參數和傳回結果作為一個鍵值對存放在緩存中,等到下次利用同樣的參數來調用該方法時将不再執行該方法,而是直接從緩存中擷取結果進行傳回。是以在使用Spring   Cache的時候我們要保證我們緩存的方法對于相同的方法參數要有相同的傳回結果。使用Spring Cache需要我們做兩方面的事:1.聲明某些方法使用緩存 2.配置Spring對Cache的支援

   Spring為我們提供了幾個注解來支援Spring Cache。其核心主要是@Cacheable和@CacheEvict。使用@Cacheable标記的方法在執行後Spring Cache将緩存其傳回結果,而使用@CacheEvict标記的方法會在方法執行前或者執行後移除Spring Cache中的某些元素。

這裡隻介紹幾個常用的屬性。

基于注解的相關概念:

[email protected] 

    可以标記在一個方法上,也可以标記在一個類上。當标記在一個方法上時表示該方法是支援緩存的,當标記在一個類上時則表示該類所有的方法都是支援緩存的。對于一個支援緩存的方法,Spring會在其被調用後将其傳回值緩存起來,以保證下次利用同樣的參數來執行該方法時可以直接從緩存中擷取結果,而不需要再次執行該方法。Spring在緩存方法的傳回值時是以鍵值對進行緩存的,值就是方法的傳回結果,至于鍵的話,Spring又支援兩種政策,預設政策和自定義政策。

    需要注意的是當一個支援緩存的方法在對象内部被調用時是不會觸發緩存功能的。@Cacheable可以指定三個屬性,value、key和condition。

[email protected]

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

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

[email protected]

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

屬性一 :value

    必須指定的,其表示目前方法的傳回值是會被緩存在哪個Cache上的,對應Cache的名稱,為ehcache.xml中的<cache name="myCache"/> 。其可以是一個Cache也可以是多個Cache,當需要指定多個Cache時其是一個數組。

屬性二 :key

    緩存的Key,當我們沒有指定該屬性時,Spring将使用預設政策生成key(表示使用方法的參數類型及參數值作為key),key屬性是用來指定Spring緩存方法的傳回結果時對應的key的。該屬性支援SpringEL表達式。我們還可以自定義政策:自定義政策是指我們可以通過Spring的EL表達式來指定我們的key。這裡的EL表達式可以使用方法參數及它們對應的屬性。使用方法參數時我們可以直接使用“#參數名”或者“#p參數index”

    key的生成政策有兩種:一種是預設政策,一種是自定義政策

       ¹預設的key生成政策是通過KeyGenerator生成的,其預設政策如下:

            1.如果方法沒有參數,則使用0作為key。

            2.如果隻有一個參數的話則使用該參數作為key。

            3.如果參數多餘一個的話則使用所有參數的hashCode作為key

        ²自定義政策是指我們可以通過Spring的EL表達式來指定我們的key。這裡的EL表達式可以使用方法參數及它們對應的屬性。使用方法參數時我們可以直接使用“#參數名”或者“#p參數index

屬性三 :condition

    有的時候我們可能并不希望緩存一個方法所有的傳回結果。通過condition屬性可以實作這一功能。

condition屬性預設為空,表示将緩存所有的調用情形。其值是通過SpringEL表達式來指定的,當為true時表示進行緩存處理;當為false時表示不進行緩存處理,即每次調用該方法時該方法都會執行一次。如下示例表示隻有當user的id為偶數時才會進行緩存

              @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;

              }

屬性四 :allEntries

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

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

              public void delete(Integer id) {

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

              }

屬性五 :beforeInvocation

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

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

             public void delete(Integer id) {

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

             }

注意我們也可以使用ehcache的去除政策最近使用(LRU)"政策,其它還有先入先出FIFO,最少使用LFU,較少使用LRU

基于注解配置:

       配置Spring對基于注解的Cache的支援,首先我們需要在Spring的配置檔案中引入cache命名空間,其次通過<cache:annotation-driven />就可以啟用Spring對基于注解的Cache的支援。

       <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等緩存注解。

基于XML配置:

<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>
           

二.EhCache 是一個純Java的程序内緩存架構,具有快速、精幹等特點。

ehcache官網:http://www.ehcache.org/ 可以下載下傳文檔看看,裡面寫的很清楚。

主要的特性有:

1. 快速

2. 簡單

3. 多種緩存政策

4. 緩存資料有兩級:記憶體和磁盤,是以無需擔心容量問題

5. 緩存資料會在虛拟機重新開機的過程中寫入磁盤

6. 可以通過RMI、可插入API等方式進行分布式緩存

7. 具有緩存和緩存管理器的偵聽接口

8. 支援多緩存管理器執行個體,以及一個執行個體的多個緩存區域

9. 提供Hibernate的緩存實作

ehcache.xml:裡面的注釋寫的很清楚了。

<diskStore>   : 當記憶體緩存中對象數量超過maxElementsInMemory時,将緩存對象寫到磁盤緩存中(需對象實作序列化接口)  

<diskStore path="">     : 用來配置磁盤緩存使用的實體路徑,Ehcache磁盤緩存使用的檔案字尾名是*.data和*.index  

name : "緩存名稱,cache的唯一辨別(ehcache會把這個cache放到HashMap裡)  

maxElementsInMemory  : 緩存最大個數。

eternal="false"   : 對象是否永久有效,一但設定了,timeout将不起作用。 (必須設定)

maxEntriesLocalHeap="1000"  : 堆記憶體中最大緩存對象數,0沒有限制(必須設定)

maxEntriesLocalDisk= "1000"   : 硬碟最大緩存個數。 

overflowToDisk="false"   : 當緩存達到maxElementsInMemory值是,是否允許溢出到磁盤(必須設定)(記憶體不足時,是否啟用磁盤緩存。)

diskSpoolBufferSizeMB  : 這個參數設定DiskStore(磁盤緩存)的緩存區大小。預設是30MB。每個Cache都應該有自己的一個緩沖區。 

diskPersistent="false"  : 磁盤緩存在JVM重新啟動時是否保持(預設為false)

timeToIdleSeconds="0"  : 導緻元素過期的通路間隔(秒為機關),即當緩存閑置n秒後銷毀。 當eternal為false時,這個屬性才有效,0表示可以永遠空閑,預設為0

timeToLiveSeconds="600"   : 元素在緩存裡存在的時間(秒為機關),即當緩存存活n秒後銷毀. 0 表示永遠存在不過期

memoryStoreEvictionPolicy="LFU" : 當達到maxElementsInMemory時,如何強制進行驅逐預設使用"最近使用(LRU)"政策,其它還有先入先出FIFO,最少使用LFU,較少使用LRU

diskExpiryThreadIntervalSeconds :磁盤失效線程運作時間間隔,預設是120秒。

clearOnFlush   : 記憶體數量最大時是否清除。

<?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"/>     
   <defaultCache
            maxEntriesLocalHeap="10000"
            eternal="false"
            overflowToDisk="false" 
            timeToIdleSeconds="120"
            timeToLiveSeconds="120"
            diskSpoolBufferSizeMB="30"
            maxEntriesLocalDisk="10000000"
            diskExpiryThreadIntervalSeconds="120"
            memoryStoreEvictionPolicy="LRU">
        <persistence strategy="localTempSwap"/>
    </defaultCache>
   
    <cache name="myCache"
           maxEntriesLocalHeap="10000"
           maxEntriesLocalDisk="1000"
           eternal="false"
           diskSpoolBufferSizeMB="30"
           timeToIdleSeconds="300"
           timeToLiveSeconds="600"
           memoryStoreEvictionPolicy="LFU"
           transactionalMode="off">
        <persistence strategy="localTempSwap"/>
    </cache>
   
</ehcache>
           

參考:使用spring cache和ehcache之前必須了解的 

繼續閱讀