最原始的進行緩存的方式:spring
最原始的使用緩存的方式是經過一個全局map保存獲取過的數據,下次獲取數據時先從map中提取,若是有就直接返回,若是沒有就從數據庫中去讀取,而後放入map中,固然,在作更新操做時須要同步更新這個map中的數據。這種方式雖然原始,可是在一些簡單的場景下已經夠用了,好比Java的類加載器就是使用的這種方式緩存加載過的class。數據庫
經過ehcache以編程方式使用緩存:編程
跟上面的方式相同,可是緩存經過ehcache去管理,固然比使用map有N多種好處,好比緩存太大了快達到上限以後,將哪一部分緩存清除出去。這種方式徹底是經過代碼的方式使用ehcache緩存,雖然自由,卻也很麻煩;有些好比單純的場景下,不須要如此麻煩,直接經過註解就好了。緩存
之前在Spring項目中要經過註解的方式使用緩存,好比藉助一個jar包:ehcache-spring-annotations.jar,能夠在googlecode上面下載,不過已經好久沒有更新過了。svn
使用ehcache-spring-annotations.jarthis
Spring配置文件:google
Xml代碼 spa
<?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:ehcache= "http://ehcache-spring-annotations.googlecode.com/svn/schema/ehcache-spring" xsi:schemaLocation= " http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://ehcache-spring-annotations.googlecode.com/svn/schema/ehcache-spring http://ehcache-spring-annotations.googlecode.com/svn/schema/ehcache-spring/ehcache-spring-1.1.xsd"> <ehcache:annotation-driven cache-manager ="ehcacheManager" /> <ehcache:config cache-manager = "ehcacheManager"> <ehcache:evict-expired-elements interval= "60" /> </ehcache:config > <bean id = "ehcacheManager" class= "org.springframework.cache.ehcache.EhCacheManagerFactoryBean" > <property name = "configLocation" value= "classpath:ehcache.xml" /> </bean > </beans>
共有兩個註解:設計
在查詢的方法上使用緩存:code
Java代碼
@Cacheable(cacheName="platform.config")
在刪除或更新的方法上清空緩存:
Java代碼
@TriggersRemove(cacheName="platform.config", when=When.AFTER_METHOD_INVOCATION , removeAll=true) public void updateConfig(SystemConfig config) { this.configDao.update(config); }
對於這種方式,這裏不作詳細探討。
在Spring 3.1之前,必須藉助以上jar包才能支持以註解的方式使用緩存,可是從Spring 3.1開始,已經提供了原生的註解支持,固然也更增強大。
Spring 3.1及以上版本的原生註解支持
Spring配置文件:
Xml代碼
<?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.xsd http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd"> <cache:annotation-driven cache-manager ="ehcacheCacheManager" /> <bean id = "ehcacheCacheManager" class= "org.springframework.cache.ehcache.EhCacheCacheManager" p:cacheManager-ref= "ehcacheManager" /> <bean id = "ehcacheManager" class= "org.springframework.cache.ehcache.EhCacheManagerFactoryBean" > <property name = "configLocation" value= "classpath:ehcache.xml" /> </bean> </beans>
Spring原生的緩存的註解共有四個:
- @Cacheable :應用到讀取數據的方法上,便可緩存的方法,如查找方法:先從緩存中讀取,若是沒有再調用方法獲取數據,而後把數據添加到緩存中
- @CacheEvict :即應用到移除數據的方法上,如刪除方法,調用方法時會從緩存中移除相應的數據
- @CachePut :應用到寫數據的方法上,如新增/修改方法,調用方法時會自動把相應的數據放入緩存
- @Caching :上面三種註解配置方法時,一個方法只能使用三者之一。若是要使用多個,則須要使用@Caching
在查詢的方法上使用緩存:
Java代碼
@Cacheable(value="platform.config" ) public String querySingleConfigValue(String configKey) {}
指定key的生成規則:
Java代碼
@Cacheable(value="platform.config", value="#configKey") public String querySingleConfigValue(String configKey) {}
緩存的key的生成:
若是在Cache註解上沒有指定key的話,會使用KeyGenerator生成一個key,默認提供了DefaultKeyGenerator生成器(Spring4以後使用SimpleKeyGenerator)。
若是隻有一個參數,就使用該參數做爲key,不然使用SimpleKey做爲key,好比有兩個入參,那麼key將是這樣的形式:SimpleKey [DEVELOPER_NAME,chenfeng]。
能夠在Cache註解上指定key的生成規則,經過SpEL表達式指定規則。詳細能夠參考:Spring Cache抽象詳解
若是沒有入參,又沒有指定key,那麼key將是「SimpleKey []」,這將很容易形成緩存的混亂,並且也不利於在更新的時候進行緩存的更新或移除。
下面的代碼入參是:SimpleKey []
Java代碼
@Cacheable (value = PlatformPrivateConstants.CACHE_NAME_CONFIG) public List<SystemConfig> queryAllSingleConfigs() { }
下面的代碼入參是:SimpleKey [DEVELOPER_NAME,chenfeng]
Java代碼
@Cacheable (value = PlatformPrivateConstants.CACHE_NAME_CONFIG ) public String querySingleConfigValue(String configKey, String defaultValue) { }
在更新的方法上更新緩存:
Java代碼
@CachePut(value=PlatformPrivateConstants.CACHE_NAME_CONFIG, key="#config.configKey") public SystemConfig updateConfig(SystemConfig config) { }
經過"#config.configKey"指定以入參config對象的configKey屬性值做爲緩存的key。
更新的方法必須將要緩存的對象做爲返回值,只有這個返回的對象纔會放入緩存中,若是在返回值被返回以前篡改了其內部數據,也會被反映到緩存中。
指定正確的key以防止緩存混亂:
下面兩個方法,前者經過configKey查詢對象,後者經過configKey查詢對象中的configValue屬性值:
Java代碼
public SystemConfig querySingleConfig(String configKey) { public String querySingleConfigValue(String configKey) { }
若是不指定key,或者使用相同的key,好比:Java代碼
@Cacheable(value="platform.config") public SystemConfig querySingleConfig(String configKey) { } @Cacheable(value="platform.config") public String querySingleConfigValue(String configKey) { }
若是以"DEVELOPER_NAME"做爲configKey分別調用這兩個方法,那麼,先調用哪一個方法,則那個方法的返回值會以"DEVELOPER_NAME"做爲key存入緩存中,此時調用這兩個方法時都會從緩存中取得這個值。好比先調用了querySingleConfig方法,那麼查詢到的SystemConfig對象就會以"DEVELOPER_NAME"爲key存入緩存中,而後調用querySingleConfigValue方法,由於仍會以"DEVELOPER_NAME"做爲key從緩存中提取數據,這時就會從緩存中取得了剛纔那個SystemConfig對象,卻看成是字符串型的configValue屬性值返回了。
因此必須爲這兩個方法指定不一樣的緩存key:
Java代碼
@Cacheable(value="platform.config", key = "#configKey.concat('_object')) public SystemConfig querySingleConfig(String configKey) { } @Cacheable(value="platform.config") public String querySingleConfigValue(String configKey) { }
這樣前者使用入參configKey值後接"_object"做爲key,然後者使用入參configKey值爲key,就不會相互覆蓋了。
在更新的方法上同時更新多個緩存:
在查詢時,可能經過id查詢也可能經過name查詢,這樣在緩存中就存有兩份數據,在更新的時候就必須同時更新這兩份緩存。
Java代碼
@Cacheable(value="platform.config", key = "#configId) public SystemConfig querySingleConfig(long configId) { } @Cacheable(value="platform.config", key = "#configKey) public SystemConfig querySingleConfig(String configKey) { }
經過@Caching註解組合多個@CachePut註解,從而在調用更新方法時同時更新多份緩存(以不一樣的key存在於緩存中)
Java代碼
@Caching(put={@CachePut(value="platform.config" , key="#config.configId"), @CachePut(value="platform.config" , key="#config.configKey" )}) public SystemConfig updateConfig(SystemConfig config) { }
在更新方法上同時更新和清除緩存:
上面的方法雖然能夠同時更新多個緩存,可是必須保證這些緩存的值是徹底同樣的,好比上面兩個方法緩存的都是SystemConfig對象,而對於下面的方法就無能爲力了,由於下面的方法緩存的是SystemConfig對象的configValue屬性值,而上面的方式只能把做爲返回值的SystemConfig對象更新到緩存中。
Java代碼
@Cacheable(value="platform.config") public String querySingleConfigValue(String configKey) { }
對於這種狀況,經過@CacheEvict清除那些沒法更新的緩存,這樣下次獲取不到這些緩存就會越過緩存從數據庫查詢最新的數據。
Java代碼
@Caching(evict={@CacheEvict(value="platform.config", key="#config.configKey" )}, put={@CachePut(value="platform.config", key="#config.configId.concat('_object')"), @CachePut(value="platform.config", key="#config.configKey.concat('_object')" )}) public SystemConfig updateConfig(SystemConfig config) { }
使用緩存的業務層的設計宗旨:
使用緩存的業務類應儘可能精簡、方法應儘量少,只保留最緊要的方法。
由於進行更新操做時須要考慮每個的獲取數據的方法,必須將可能被更新操做波及的全部緩存進行更新或清除,若是獲取數據的方法過多,這個工做量將變得很大,而且很容易出錯,好比忘記了更新或清除某一份緩存,而大型項目中這種錯誤可能難以被發現。另外,保存多份緩存,也增長了ehcache的負擔,由於它須要維護這些緩存數據,好比須要判斷哪些緩存最久未使用而從緩存中移除等。
因此應該在處於低層的業務類上使用緩存,而在高層的業務類上提供豐富的獲取數據的方法,高層業務類全部操做均調用底層的業務類實現。
對於不會被頻繁調用的方法,不要進行緩存,沒有實際意義,還無謂增大ehcache的負擔,而且浪費寶貴的內存。