#spring 使用cachejava
spring 3.1 開始對cache的支持,spring-cache做用在方法上的。spring
使用Spring Cache須要咱們作兩方面的事:數組
Spring爲咱們提供了幾個註解來支持Spring Cache。其核心主要是@Cacheable
和@CacheEvict
。使用@Cacheable
標記的方法在執行後Spring Cache將緩存其返回結果,而使用@CacheEvict
標記的方法會在方法執行前或者執行後移除Spring Cache中的某些元素。下面咱們未來詳細介紹一下Spring基於註解對Cache的支持所提供的幾個註解。緩存
@Cacheable
能夠標記在一個方法上,也能夠標記在一個類上。當標記在一個方法上時表示該方法是支持緩存的,當標記在一個類上時則表示該類全部的方法都是支持緩存的。對於一個支持緩存的方法,Spring會在其被調用後將其返回值緩存起來,以保證下次利用一樣的參數來執行該方法時能夠直接從緩存中獲取結果,而不須要再次執行該方法。Spring在緩存方法的返回值時是以鍵值對進行緩存的,值就是方法的返回結果,至於鍵的話,Spring又支持兩種策略,默認策略和自定義策略,這個稍後會進行說明。須要注意的是當一個支持緩存的方法在對象內部被調用時是不會觸發緩存功能的。@Cacheable
能夠指定三個屬性,value、key和condition。ide
value屬性是必須指定的,其表示當前方法的返回值是會被緩存在哪一個Cache上的,對應Cache的名稱。其能夠是一個Cache也能夠是多個Cache,當須要指定多個Cache時其是一個數組。this
@Cacheable("cache1")//Cache是發生在cache1上的 public User find(Integer id) { returnnull; }
key屬性是用來指定Spring緩存方法的返回結果時對應的key的。該屬性支持SpringEL表達式。當咱們沒有指定該屬性時,Spring將使用默認策略生成key。咱們這裏先來看看自定義策略,至於默認策略會在後文單獨介紹。 自定義策略是指咱們能夠經過Spring的EL表達式來指定咱們的key。這裏的EL表達式可使用方法參數及它們對應的屬性。使用方法參數時咱們能夠直接使用「#參數名」或者「#p參數index」。下面是幾個使用參數做爲key的示例。spa
@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對象的屬性。如:code
@Cacheable(value={"users", "xxx"}, key="caches[1].name") public User find(User user) { returnnull; }
####1.1.3 condition屬性指定發生的條件 有的時候咱們可能並不但願緩存一個方法全部的返回結果。經過condition屬性能夠實現這一功能。condition屬性默認爲空,表示將緩存全部的調用情形。其值是經過SpringEL表達式來指定的,當爲true時表示進行緩存處理;當爲false時表示不進行緩存處理,即每次調用該方法時該方法都會執行一次。以下示例表示只有當user的id爲偶數時纔會進行緩存。xml
@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; }
在支持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能夠指定的屬性有value、key、condition、allEntries和beforeInvocation。其中value、key和condition的語義與@Cacheable對應的屬性相似。即value表示清除操做是發生在哪些Cache上的(對應Cache的名稱);key表示須要清除的是哪一個key,如未指定則會使用默認策略生成的key;condition表示清除操做發生的條件。下面咱們來介紹一下新出現的兩個屬性allEntries和beforeInvocation。
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能夠改變觸發清除操做的時間,當咱們指定該屬性值爲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不是本文描述的重點,這裏就很少贅述了。
###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 find(Integer id) { return null; }
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; }
###1.6 @CacheConfig annotation 到目前爲止,咱們已經看到,cache操做提供了許多自定義選項,這些均可以操做的基礎上進行設置。然而,一些自定義選項是很乏味的配置是否適用於類的全部操做。例如,指定cache以用於類的每一個cache操做的名稱能夠由單一類級定義來代替。這是@CacheConfig用武之地。
@CacheConfig("books") public class BookRepositoryImpl implements BookRepository { @Cacheable public Book findBook(ISBN isbn) {...} }
@CacheConfig is a class-level annotation that allows to share the cache names, the custom KeyGenerator, the custom CacheManager and finally the custom CacheResolver. Placing this annotation on the class does not turn on any caching operation.
An operation-level customization will always override a customization set on @CacheConfig. This gives therefore three levels of customizations per cache operation:
##2 配置Spring對Cache的支持
###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>
or
@Configuration @EnableCaching public class AppConfig { }
<cache:annotation-driven/>
有一個cache-manager屬性用來指定當前所使用的CacheManager對應的bean的名稱,默認是cacheManager,因此當咱們的CacheManager的id爲cacheManager時咱們能夠不指定該參數,不然就須要咱們指定了。 <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處理。更多關於Spring Aop的內容不在本文討論範疇內。
###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> <bean class="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文件做爲配置文件。。
##3 鍵的生成策略
鍵的生成策略有兩種,一種是默認策略,一種是自定義策略。 ###3.1 默認策略 默認的key生成策略是經過KeyGenerator生成的,其默認策略以下:
若是方法沒有參數,則使用0做爲key。
若是隻有一個參數的話則使用該參數做爲key。
若是參數多餘一個的話則使用全部參數的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。
##4 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>