2020国产成人精品视频,性做久久久久久久久,亚洲国产成人久久综合一区,亚洲影院天堂中文av色

分享

Spring in Action(2nd) Cache

 燮羽 2011-01-08

 

緩存


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

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

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

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

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

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

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

下面我們來修改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
}

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

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

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

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

5.7.1  配置緩存方案

雖然Spring Modules會提供一個代理來攔截方法并把結(jié)果保存到緩存,它并沒有提供一個實際的緩存解決方案,而是要依賴于第三方的緩存方案。可以使用的方案有多個,包括:

n    EHCache

n    GigaSpaces

n    JBoss Cache

n    JCS

n    OpenSymphony的OSCache

n    Tangosol的Coherence

我們?yōu)镽oadRantz程序選擇EHCache,主要是因為我以前使用它的經(jīng)驗及能夠從www.ibibio.org的Maven倉庫輕易獲得。無論使用哪個緩存方案,對于Spring Modules Cache的配置基本上都是一樣的。

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

與Spring上下文配置文件一樣,roadrantz-cache.xml也以<beans>元素為根。但為了利用Spring Modules對EHCache的支持,我們要讓<beans>元素能夠識別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列出了每個命名空間及其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

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

表5.7                                         Spring Modules的配置元素

 

配 置 元 素

用    途

<namespace:annotations>

以Java 5注解來聲明被緩存的方法

<namespace:commons-attributes>

以Jakarta通用屬性元素數(shù)據(jù)來聲明被緩存的方法

<namespace:config>

在Spring XML里配置緩存方案

<namespace:proxy>

在Spring XML里聲明一個代理來聲明被緩存的方法

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

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

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

配置EHCache

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

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

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

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

表5.8                                          EHCache的緩存配置屬性

 

屬    性

用 于 指 定

diskExpiryThreadIntervalSeconds

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

diskPersistent

磁盤緩存在VM重新啟動時是否保持(默認為false)。

eternal

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

maxElementsInMemory

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

memoryStoreEvictionPolicy

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

name

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

overflowToDisk

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

timeToIdleSeconds

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

timeToLiveSeconds

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

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

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

5.7.2  緩存的代理Bean

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

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

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

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

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

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

刷新緩存

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

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

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

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

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

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

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

如果覺得id/refId組合有些奇怪,我們還可以把目標(biāo)Bean聲明為<ehcache:proxy>的內(nèi)部Bean。舉例來說,下面就是把HibernateRantDao配置為一個內(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)匀恍枰獮槊總€要代理的Bean聲明一個<ehcache:proxy>元素,為方法聲明一個或多個<ehcache:caching>元素。對于簡單程序來說,這樣做不會有什么問題,但隨著代理緩存Bean和方法的數(shù)量不斷增加,這將意味著Spring配置里越來越多的XML。

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多