日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

Spring in Action(2nd) Cache

 燮羽 2011-01-08

 

緩存


在很多程序里,讀取數(shù)據(jù)的頻率比寫入要高得多。比如RoadRantz,訪問站點(diǎn)來(lái)查看帖子的人比張貼帖子的人要多。雖然帖子列表會(huì)隨著時(shí)間不斷增長(zhǎng),但其增長(zhǎng)速度比不上被查看的速度。

更進(jìn)一步說(shuō),RoadRantz所展示的數(shù)據(jù)對(duì)于實(shí)時(shí)性要求并不高。如果用戶在訪問站點(diǎn)時(shí)看到了稍微過時(shí)一點(diǎn)的帖子列表,并不會(huì)產(chǎn)生太多負(fù)面影響,他們會(huì)稍后再返回站點(diǎn)來(lái)查看更新的帖子列表,這樣做并不會(huì)有太大問題。

盡管如此,DAO每次收到關(guān)于帖子列表的請(qǐng)求時(shí),都會(huì)訪問數(shù)據(jù)庫(kù)來(lái)獲得最新的數(shù)據(jù)(經(jīng)常會(huì)得到與上次請(qǐng)求一樣的數(shù)據(jù))。

數(shù)據(jù)庫(kù)操作通常都是程序性能的最大瓶頸。對(duì)于負(fù)載很大的程序來(lái)說(shuō),針對(duì)高度優(yōu)化的數(shù)據(jù)源進(jìn)行的最簡(jiǎn)單查詢都可能會(huì)產(chǎn)生性能問題。

均衡考慮數(shù)據(jù)變化的頻率以及查詢數(shù)據(jù)庫(kù)所付出的性能代價(jià),總是從數(shù)據(jù)庫(kù)獲取最新數(shù)據(jù)似乎并不明智,而對(duì)頻繁訪問(但不頻繁更新)的數(shù)據(jù)進(jìn)行緩存則顯得更加合理。

從表面上看,緩存似乎相當(dāng)簡(jiǎn)單:在獲取一些信息之后,把它保存到本地(和更便于訪問的)位置,從而便于下次需要時(shí)使用。但是,手工實(shí)現(xiàn)緩存是很麻煩的。以HibernateRantDao的getRantsForDay()方法為例:
public List<Rant> getRantsForDay(Date day) {
      return getHibernateTemplate().find("from " + RANT +" where postedDate = ?", day);
}

這個(gè)方法就非常適合緩存。我們不可能讓時(shí)間倒轉(zhuǎn),到過去的某一天來(lái)添加帖子。只要被查詢的不是今天,對(duì)其他任意一天進(jìn)行查詢而返回的帖子列表都是一樣的,也就沒有必要總是對(duì)數(shù)據(jù)庫(kù)進(jìn)行操作來(lái)返回過去某一天的帖子列表。我們只需查詢數(shù)據(jù)庫(kù)一次,然后就可以記住結(jié)果,以備下次查詢時(shí)使用。

下面我們來(lái)修改getRantsForDay(),使用某種自制形式的緩存:

public List<Rant> getRantsForDay(Date day) {
List<Rant> cachedResult =
rantCache.lookup("getRantsForDay", day);
if(cachedResult != null) {
return cachedResult;
}
cachedResult = getHibernateTemplate().find("from " + RANT +
" where postedDate = ?", day);
rantCache.store("getRantsForDay", day, cachedResult);
return cachedResult
}

這個(gè)版本的getRantsForDay()很不好用。這個(gè)方法的實(shí)際作用是查詢指定日期的帖子,但其中大量代碼都被用于處理緩存了。而且,它還沒有直接處理緩存的一些復(fù)雜情況,比如緩存過期、刷新或溢出。

文本框:
圖5.13 Spring Module緩存模塊攔截對(duì)bean方法的調(diào)用,從緩存獲取數(shù)據(jù)來(lái)實(shí)現(xiàn)快速數(shù)據(jù)訪問,
以此減少對(duì)數(shù)據(jù)庫(kù)的實(shí)際訪問。幸運(yùn)的是,Spring程序有一種更優(yōu)雅的緩存解決方案。Spring Modules項(xiàng)目(http://springmodules.dev.)通過切面提供了緩存,它把通知應(yīng)用于Bean方法來(lái)透明地對(duì)其結(jié)果進(jìn)行緩存,而不是明確地指定要被緩存的方法。

如圖5.13所示,Spring Modules對(duì)于緩存的支持涉及到一個(gè)代理,它攔截對(duì)Spring管理的Bean的一個(gè)或多個(gè)方法的調(diào)用。當(dāng)一個(gè)被代理的方法被調(diào)用時(shí),Spring Modules Cache首先查閱一個(gè)緩存來(lái)判斷這個(gè)方法是否已經(jīng)被使用同樣參數(shù)調(diào)用過,如果是,它會(huì)返回緩存里的值,實(shí)際的方法并不會(huì)被調(diào)用;否則,實(shí)際方法會(huì)被調(diào)用,其返回值會(huì)被保存到緩存里,以備方法下一次被調(diào)用時(shí)使用。

在這一小節(jié)里,我們將使用Spring Modules Cache為RoadRantz的DAO層添加緩存功能,這樣會(huì)讓程序具有更好的性能,讓繁忙的數(shù)據(jù)庫(kù)輕松一些。

5.7.1  配置緩存方案

雖然Spring Modules會(huì)提供一個(gè)代理來(lái)攔截方法并把結(jié)果保存到緩存,它并沒有提供一個(gè)實(shí)際的緩存解決方案,而是要依賴于第三方的緩存方案??梢允褂玫姆桨赣卸鄠€(gè),包括:

n    EHCache

n    GigaSpaces

n    JBoss Cache

n    JCS

n    OpenSymphony的OSCache

n    Tangosol的Coherence

我們?yōu)镽oadRantz程序選擇EHCache,主要是因?yàn)槲乙郧笆褂盟慕?jīng)驗(yàn)及能夠從www.ibibio.org的Maven倉(cāng)庫(kù)輕易獲得。無(wú)論使用哪個(gè)緩存方案,對(duì)于Spring Modules Cache的配置基本上都是一樣的。

首先要做的是新建一個(gè)Spring配置文件來(lái)聲明緩存。雖然可以把Spring Modules Cache配置放到RoadRantz程序加載的任意一個(gè)Spring上下文配置文件里,但最好還是把它們分開,所以我們要?jiǎng)?chuàng)建roadrantz-cache.xml來(lái)保存緩存的配置。

與Spring上下文配置文件一樣,roadrantz-cache.xml也以<beans>元素為根。但為了利用Spring Modules對(duì)EHCache的支持,我們要讓<beans>元素能夠識(shí)別ehcache命名空間:

<beans xmlns="http://www./schema/beans"
xmlns:xsi="http://www./2001/XMLSchema-instance"
xmlns:ehcache="http://www./schema/ehcache"
xsi:schemaLocation="http://www./schema/beans
http://www./schema/beans/spring-beans-2.0.xsd
http://www./schema/ehcache
http://www./schema/cache/
springmodules-ehcache.xsd">

</beans>

我們?yōu)镽oadRantz程序選擇的是EHCache,如果想使用其他緩存方案,需要把Spring Modules命名究竟和規(guī)劃聲明修改為相應(yīng)的內(nèi)容。表5.6列出了每個(gè)命名空間及其URI和規(guī)劃URI。

表5.6                   Spring Modules所支持的緩存方案的命名空間及規(guī)劃

 
命 名 空 間

命名空間URI

規(guī)劃URI

ehcache

http://www./schema/ehcache

http://www./schema/cache/springmodules- ehcache.xsd

gigaspaces

http://www./ schema/gigaspaces

http://www./schema/cache/springmodules- gigaspaces.xsd

jboss

http://www./ schema/jboss

http://www./schema/cache/springmodules- jboss.xsd

jcs

http://www./ schema/jcs

http://www./schema/cache/springmodules-jcs.xsd

oscache

http://www./schema/oscache

http://www./schema/cache/springmodules- oscache.xsd

tangosol

http://www./schema/tangosol

http://www./schema/cache/springmodules- tangosol.xsd

無(wú)論選擇哪種緩存,都可以使用一些Spring配置元素在Spring里對(duì)緩存進(jìn)行配置。表5.7列出了這些元素。

表5.7                                         Spring Modules的配置元素

 

配 置 元 素

用    途

<namespace:annotations>

以Java 5注解來(lái)聲明被緩存的方法

<namespace:commons-attributes>

以Jakarta通用屬性元素?cái)?shù)據(jù)來(lái)聲明被緩存的方法

<namespace:config>

在Spring XML里配置緩存方案

<namespace:proxy>

在Spring XML里聲明一個(gè)代理來(lái)聲明被緩存的方法

在使用EHCache作為緩存方案時(shí),需要告訴Spring到哪里尋找EHCache配置文件[5],這正是<ehcache:config>元素的用途所在:

<ehcache:config configLocation="classpath:ehcache.xml" />

在此對(duì)configLocation屬性的設(shè)置告訴Spring從程序類路徑的根位置加載EHCache的配置。

配置EHCache

我們已經(jīng)配置了ehcache.xml文件,如程序清單5.12所示。

 程序清單5.12 在ehcache.xml里配置EHCache。

在這段代碼里,我們配置了兩個(gè)緩存讓EHCache進(jìn)行管理。<defulatCache>元素是必須有的,描述了在沒有找到其他緩存情況下所使用的緩存。<cache>元素定義了另一個(gè)緩存,可以在ehcache.xml里出現(xiàn)0次或多次(每次針對(duì)定義的一個(gè)緩存)。在此,我們只定義了rantzCache作為惟一的非默認(rèn)緩存。

<defaultCache>和<cache>里指定的屬性描述了緩存的行為。表5.8列出在配置EHCache緩存時(shí)可以使用的屬性。

表5.8                                          EHCache的緩存配置屬性

 

屬    性

用 于 指 定

diskExpiryThreadIntervalSeconds

磁盤過期線程運(yùn)行的頻率(以秒為單位),也就是磁盤存留的緩存清理過期項(xiàng)目的頻率(默認(rèn)是120秒)。

diskPersistent

磁盤緩存在VM重新啟動(dòng)時(shí)是否保持(默認(rèn)為false)。

eternal

元素是否永恒。如果是永恒的,就永遠(yuǎn)不會(huì)過期(必須設(shè)置)。

maxElementsInMemory

內(nèi)存能夠被緩存的最大元素?cái)?shù)量(必須設(shè)置)。

memoryStoreEvictionPolicy

當(dāng)達(dá)到maxElementsInMemory時(shí),如何強(qiáng)制進(jìn)行驅(qū)逐。默認(rèn)使用“最近使用(LRU)”策略,還可以使用“先入先出(FIFO)”和“較少使用(LFU)”策略。(默認(rèn)是LRU。)

name

緩存的名稱。(對(duì)于<cache>必須設(shè)置。)

overflowToDisk

當(dāng)內(nèi)存緩存達(dá)到maxElementsInMemory時(shí),是否可以溢出到磁盤。(必須設(shè)置。)

timeToIdleSeconds

導(dǎo)致元素過期的訪問間隔(以秒為單位)。設(shè)置為0表示元素可以永遠(yuǎn)空閑。(默認(rèn)值是0。)

timeToLiveSeconds

元素在緩存里可以存在的時(shí)間(以秒為單位)。設(shè)置為0表示元素可以在緩存里永遠(yuǎn)存在而不過期。(默認(rèn)值是0。)

對(duì)于RoadRantz程序,我們配置了一個(gè)默認(rèn)緩存(這是EHCache要求的),還配置了一個(gè)名為rantzCache的緩存作為主緩存。兩個(gè)緩存都設(shè)置為最多可以容納500個(gè)元素(不過期),訪問頻率最低的元素會(huì)被踢出,不允許磁盤溢出[6]

在Spring程序上下文里配置的EHCache之后,就可以聲明哪個(gè)Bean和方法應(yīng)該對(duì)結(jié)果進(jìn)行緩存。首先,我們來(lái)聲明一個(gè)代理來(lái)緩存RoadRantz DAO層里方法的返回值。

5.7.2  緩存的代理Bean

我們已經(jīng)知道HibernateRantDao里的getRantsForDay()方法很適合進(jìn)行緩存。再回到Spring上下文定義,我們要使用<ehcache:proxy>元素把一個(gè)代理包裹到HibernateRantDao,從而緩存從getRantsForDay()返回的全部?jī)?nèi)容:

<ehcache:proxy id="rantDao" refId="rantDaoTarget">
           <ehcache:caching methodName="getRantsForDay" cacheName="rantzCache" />
     </ehcache:proxy>

<ehcache:caching>元素聲明哪個(gè)方法要被攔截、其返回值要保存到哪個(gè)緩存。本例中,methodName被設(shè)置為getRantsForDay(),要使用的緩存是rantzCache。

我們可以根據(jù)需要在<ehcache:proxy>里聲明多個(gè)<ehcache:cacing>來(lái)描述Bean方法的緩存。我們可以讓一個(gè)<ehcache:caching>用于所有被緩存的方法,也可以使用通配符為一個(gè)<ehcache:caching>元素指定多個(gè)方法。比如下面的<ehcache:caching>元素會(huì)代理緩存全部名稱由get開頭的方法:

<ehcache:caching methodName="get*" cacheName="rantzCache" />

把數(shù)據(jù)放到緩存里只完成了一半的工作。在經(jīng)過一段時(shí)間之后,緩存里一定會(huì)包含大量數(shù)據(jù),其中很多已經(jīng)沒有意義了。最后,這些數(shù)據(jù)應(yīng)該被清出緩存,數(shù)據(jù)緩存周期重新開始。下面我們來(lái)看一看如何在方法調(diào)用時(shí)刷新緩存。

刷新緩存

<ehcache:caching>元素聲明的是要向緩存中添加數(shù)據(jù)的方法,而<ehcache:flushing>元素聲明了會(huì)清空緩存的方法。舉例來(lái)說(shuō),假設(shè)我們想在saveRant()方法被調(diào)用時(shí)清空rantzCache緩存,那么就應(yīng)該使用如下的<ehcache:flushing>元素:

<ehcache:flushing methodName="saveRant" cacheName="rantzCache" />

在默認(rèn)情況下,cacheName屬性里指定的緩存會(huì)在methodName被調(diào)用之后清空,但利用when屬性可以指定清空的時(shí)機(jī):

<ehcache:flushing methodName="saveRant" cacheName="rantzCache" when="before" />

把when屬性設(shè)置為before可以讓緩存在saveRant()被調(diào)用之前清空。

聲明一個(gè)被代理的內(nèi)部Bean

注意<ehcache:proxy>的id和refId屬性。由<ehcache:proxy>生成的代理的id是rantDao,然而這是HibernateRantDao Bean的id,因此,我們需要把這個(gè)真正的Bean重命名為rantDaoTarget(由refId屬性指定)。(這與傳統(tǒng)Spring AOP代理及其目標(biāo)的命名方式是一樣的,詳情請(qǐng)見4.2.3小節(jié)。)

如果覺得id/refId組合有些奇怪,我們還可以把目標(biāo)Bean聲明為<ehcache:proxy>的內(nèi)部Bean。舉例來(lái)說(shuō),下面就是把HibernateRantDao配置為一個(gè)內(nèi)部Bean的<ehcache:proxy>:

<ehcache:proxy id="rantDao">
          <bean class="com.roadrantz.dao.HibernateRantDao">
             <property name="sessionFactory" ref="sessionFactory" />
          </bean>
          <ehcache:caching methodName="getRantsForDay" cacheName="rantzCache" />
     </ehcache:proxy>

即使使用了內(nèi)部Bean,我們?nèi)匀恍枰獮槊總€(gè)要代理的Bean聲明一個(gè)<ehcache:proxy>元素,為方法聲明一個(gè)或多個(gè)<ehcache:caching>元素。對(duì)于簡(jiǎn)單程序來(lái)說(shuō),這樣做不會(huì)有什么問題,但隨著代理緩存Bean和方法的數(shù)量不斷增加,這將意味著Spring配置里越來(lái)越多的XML。

如果對(duì)內(nèi)部Bean的方法仍然感到不快,或是需要代理多個(gè)要緩存的Bean,我們可以考慮使用Spring Modules對(duì)注解聲明緩存的支持。接下來(lái),讓我們忘記<ehcache:proxy>,看一看Spring Modules如何支持注解驅(qū)動(dòng)的緩存。

5.7.3  注解驅(qū)動(dòng)的緩存

除了前面介紹的基于XML的緩存配置,Spring Modules還支持使用代碼級(jí)元數(shù)據(jù)聲明緩存。這種支持有兩種形式:

    Java 5注解:如果目標(biāo)環(huán)境是Java 5平臺(tái),這就是很理想的解決方案。

    Jakarta公共屬性:如果目標(biāo)環(huán)境是Java 5以前的平臺(tái),就應(yīng)該選擇它。

對(duì)于RoadRantz程序來(lái)說(shuō),其目標(biāo)環(huán)境是Java 5,所以我們要使用Java 5注解來(lái)聲明DAO層的緩存。對(duì)于緩存,Spring Modules提供了兩個(gè)注解:

    @Cacheable:聲明一個(gè)方法的返回值應(yīng)該被緩存。

    @CacheFlush:聲明一個(gè)方法是清空緩存的觸發(fā)器。

利用@Cacheable注解,我們可以像下面這樣把getRantsForDay()聲明為要被緩存的:

@Cacheable(modelId="rantzCacheModel")
        public List<Rant> getRantsForDay(Date day) {
        return getHibernateTemplate().find("from " + RANT +
        " where postedDate = ?", day);
     }

modelId屬性指定用于緩存方法返回值的模型,稍后我們介紹說(shuō)明如何定義緩存模型,現(xiàn)在先來(lái)看一看如何使用@CacheFlush來(lái)指定saveRant()被調(diào)用時(shí)的緩存清空操作:

@CacheFlush(modelId="rantzFlushModel")
     public void saveRant(Rant rant) {
            getHibernateTemplate().saveOrUpdate(rant);
     }

modelId屬性指定的刷新模型會(huì)在saveRant()方法被調(diào)用時(shí)被清空。

既然說(shuō)到緩存模型和刷新模型,那么它們是從何而來(lái)的呢?<ehcache:annotateions>元素被用于啟動(dòng)Spring Modules對(duì)注解的支持,我們會(huì)在roadrantzcache.xml文件里像下面這樣配置它:

<ehcache:annotations>
        <ehcache:caching id="rantzCacheModel"  cacheName="rantzCache" />
    </ehcache:annotations>

在<ehcache:annotateions>元素里,必須配置至少一個(gè)<ehcache:caching>元素,它就定義了一個(gè)緩存模型。簡(jiǎn)單來(lái)說(shuō),緩存模型基本上就是對(duì)ehcache.xml里配置的一個(gè)緩存的引用。本例中,我們把rantzCacheModel與名為rantzCache的緩存關(guān)聯(lián)起來(lái),這樣一來(lái),任何modelId是rantzCacheModel的@Cacheable都會(huì)使用名為rantzCache的緩存。

刷新模型與緩存模型相當(dāng)類似,只是它引用的是要被刷新的緩存。下面使用<ehcache:flushing>元素創(chuàng)建一個(gè)名為rantzFlushModel的刷新模型:

<ehcache:annotations>
           <ehcache:caching id="rantzCacheModel" cacheName="rantzCache" />
           <ehcache:flushing id="rantzFlushModel" cacheName="rantzCache" />
    </ehcache:annotations>

設(shè)置緩存模型與刷新模型的不同之處在于,刷新模型不僅決定要清空哪個(gè)緩存,還決定了何時(shí)清空。在默認(rèn)情況下,緩存是在@CacheFlush注解的方法被調(diào)用之后清空的,但我們可以通過指定<ehcache:flushing>的when屬性來(lái)改變:

<ehcache:annotations>
            <ehcache:caching id="rantzCacheModel" cacheName="rantzCache" />
            <ehcache:flushing id="rantzFlushModel" cacheName="rantzCache" when="before" />
     </ehcache:annotations>

把when屬性設(shè)置為before之后,緩存就會(huì)@CacheFlush注解的方法被調(diào)用之前清空。

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多