轉載請註明出處:http://blog.csdn.net/dongdong9223/article/details/50538085
本文出自【我是幹勾魚的博客】css
EhCache 是一個純Java的進程內緩存框架。具備高速、精幹等特色,是Hibernate中默認的CacheProvider。java
Ehcache是一種普遍使用的開源Java分佈式緩存。主要面向通用緩存,Java EE和輕量級容器。它具備內存和磁盤存儲,緩存載入器,緩存擴展,緩存異常處理程序,一個gzip緩存servlet過濾器,支持REST和SOAP api等特色。git
Ehcache最初是由Greg Luck於2003年開始開發。github
2009年,該項目被Terracotta購買。軟件仍然是開源,但一些新的主要功能(好比,高速可從新啓動性之間的一致性的)僅僅能在商業產品中使用,好比Enterprise EHCache and BigMemory。web
維基媒體Foundationannounced眼下使用的就是Ehcache技術。spring
總之Ehcache仍是一個不錯的緩存技術,咱們來看看Spring搭配Ehcache是怎樣實現的。數據庫
系統結果例如如下:express
有這幾部分的結合:api
src:java代碼,包含攔截器,調用接口,測試類緩存
src/cache-bean.xml:配置Ehcache。攔截器,以及測試類等信息相應的bean
src/ehcache.xml:Ehcache緩存配置信息
WebRoot/lib:庫
代碼中首先配置了兩個攔截器:
第一個攔截器爲:
com.test.ehcache.CacheMethodInterceptor
內容例如如下:
package com.test.ehcache;
import java.io.Serializable;
import net.sf.ehcache.Cache;
import net.sf.ehcache.Element;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert;
public class CacheMethodInterceptor implements MethodInterceptor, InitializingBean {
private Cache cache;
public void setCache(Cache cache) {
this.cache = cache;
}
public CacheMethodInterceptor() {
super();
}
/** * 攔截ServiceManager的方法,並查找該結果是否存在,假設存在就返回cache中的值, * 不然,返回數據庫查詢結果,並將查詢結果放入cache */
public Object invoke(MethodInvocation invocation) throws Throwable {
//獲取要攔截的類
String targetName = invocation.getThis().getClass().getName();
//獲取要攔截的類的方法
String methodName = invocation.getMethod().getName();
//得到要攔截的類的方法的參數
Object[] arguments = invocation.getArguments();
Object result;
//建立一個字符串。用來作cache中的key
String cacheKey = getCacheKey(targetName, methodName, arguments);
//從cache中獲取數據
Element element = cache.get(cacheKey);
if (element == null) {
//假設cache中沒有數據,則查找非緩存,好比數據庫,並將查找到的放入cache
result = invocation.proceed();
//生成將存入cache的key和value
element = new Element(cacheKey, (Serializable) result);
System.out.println("-----進入非緩存中查找,好比直接查找數據庫,查找後放入緩存");
//將key和value存入cache
cache.put(element);
} else {
//假設cache中有數據,則查找cache
System.out.println("-----進入緩存中查找,不查找數據庫。緩解了數據庫的壓力");
}
return element.getValue();
}
/** * 得到cache的key的方法,cache的key是Cache中一個Element的惟一標識, * 包含包名+類名+方法名,如:com.test.service.TestServiceImpl.getObject */
private String getCacheKey(String targetName, String methodName,
Object[] arguments) {
StringBuffer sb = new StringBuffer();
sb.append(targetName).append(".").append(methodName);
if ((arguments != null) && (arguments.length != 0)) {
for (int i = 0; i < arguments.length; i++) {
sb.append(".").append(arguments[i]);
}
}
return sb.toString();
}
/** * implement InitializingBean,檢查cache是否爲空 70 */
public void afterPropertiesSet() throws Exception {
Assert.notNull(cache,
"Need a cache. Please use setCache(Cache) create it.");
}
}
CacheMethodInterceptor用來攔截以「get」開頭的方法,注意這個攔截器是先攔截,後運行原調用接口。
另外一個攔截器:
com.test.ehcache.CacheAfterReturningAdvice
詳細內容:
package com.test.ehcache;
import java.lang.reflect.Method;
import java.util.List;
import net.sf.ehcache.Cache;
import org.springframework.aop.AfterReturningAdvice;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert;
public class CacheAfterReturningAdvice implements AfterReturningAdvice, InitializingBean {
private Cache cache;
public void setCache(Cache cache) {
this.cache = cache;
}
public CacheAfterReturningAdvice() {
super();
}
public void afterReturning(Object arg0, Method arg1, Object[] arg2,
Object arg3) throws Throwable {
String className = arg3.getClass().getName();
List list = cache.getKeys();
for (int i = 0; i < list.size(); i++) {
String cacheKey = String.valueOf(list.get(i));
if (cacheKey.startsWith(className)) {
cache.remove(cacheKey);
System.out.println("-----清除緩存");
}
}
}
public void afterPropertiesSet() throws Exception {
Assert.notNull(cache,
"Need a cache. Please use setCache(Cache) create it.");
}
}
CacheAfterReturningAdvice用來攔截以「update」開頭的方法,注意這個攔截器是先運行原調用接口,後被攔截。
接口名稱爲:
com.test.service.ServiceManager
詳細內容例如如下:
package com.test.service;
import java.util.List;
public interface ServiceManager {
public List getObject();
public void updateObject(Object Object);
}
實現類名稱爲:
com.test.service.ServiceManagerImpl
詳細內容例如如下:
package com.test.service;
import java.util.ArrayList;
import java.util.List;
public class ServiceManagerImpl implements ServiceManager {
@Override
public List getObject() {
System.out.println("-----ServiceManager:緩存Cache內不存在該element,查找數據庫,並放入Cache!"); return null; } @Override public void updateObject(Object Object) { System.out.println("-----ServiceManager:更新了對象,這個類產生的cache都將被remove!"); } }
測試類名稱爲:
com.test.service.TestMain
詳細內容爲:
package com.test.service;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestMain {
public static void main(String[] args) {
String cacheString = "/cache-bean.xml";
ApplicationContext context = new ClassPathXmlApplicationContext(
cacheString);
//獲代替理工廠proxyFactory生成的bean,以便產生攔截效果
ServiceManager testService = (ServiceManager) context.getBean("proxyFactory");
// 第一次查找
System.out.println("=====第一次查找");
testService.getObject();
// 第二次查找
System.out.println("=====第二次查找");
testService.getObject();
// 運行update方法(應該清除緩存)
System.out.println("=====第一次更新");
testService.updateObject(null);
// 第三次查找
System.out.println("=====第三次查找");
testService.getObject();
}
}
此處要注意。獲取bean是經過代理工廠proxyFactory生產的bean,這樣纔會有攔截效果。
能夠看出來,在測試類裏面設置了四次調用。運行順序爲:
cache-bean.xml用來配置Ehcache。攔截器,以及測試類等信息相應的bean,內容例如如下:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd"> <beans> <!-- 引用ehCache 的配置--> <bean id="defaultCacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"> <property name="configLocation"> <value>ehcache.xml</value> </property> </bean> <!-- 定義ehCache的工廠,並設置所使用的Cache的name。即「com.tt」 --> <bean id="ehCache" class="org.springframework.cache.ehcache.EhCacheFactoryBean"> <property name="cacheManager"> <ref local="defaultCacheManager" /> </property> <!-- Cache的名稱 --> <property name="cacheName"> <value>com.tt</value> </property> </bean> <!-- 建立緩存、查詢緩存的攔截器 --> <bean id="cacheMethodInterceptor" class="com.test.ehcache.CacheMethodInterceptor"> <property name="cache"> <ref local="ehCache" /> </property> </bean> <!-- 更新緩存、刪除緩存的攔截器 --> <bean id="cacheAfterReturningAdvice" class="com.test.ehcache.CacheAfterReturningAdvice"> <property name="cache"> <ref local="ehCache" /> </property> </bean> <!-- 調用接口,被攔截的對象 --> <bean id="serviceManager" class="com.test.service.ServiceManagerImpl" /> <!-- 插入攔截器。確認調用哪一個攔截器,攔截器攔截的方法名特色等,此處調用攔截器com.test.ehcache.CacheMethodInterceptor --> <bean id="cachePointCut" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"> <!-- 增長切面。切面爲當運行完print方法後。在運行增長的切面 --> <property name="advice"> <ref local="cacheMethodInterceptor" /> </property> <property name="patterns"> <list> <!-- ### .表示符合不論什麼單一字元 ### +表示符合前一個字元一次或屢次 ### *表示符合前一個字元零次或屢次 ### \Escape不論什麼Regular expression使用到的符號 --> <!-- .*表示前面的前綴(包含包名),意思是表示getObject方法--> <value>.*get.*</value> </list> </property> </bean> <!-- 插入攔截器,確認調用哪一個攔截器,攔截器攔截的方法名特色等。此處調用攔截器com.test.ehcache.CacheAfterReturningAdvice --> <bean id="cachePointCutAdvice" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"> <property name="advice"> <ref local="cacheAfterReturningAdvice" /> </property> <property name="patterns"> <list> <!-- .*表示前面的前綴(包含包名),意思是updateObject方法--> <value>.*update.*</value> </list> </property> </bean> <!-- 代理工廠 --> <bean id="proxyFactory" class="org.springframework.aop.framework.ProxyFactoryBean"> <!-- 說明調用接口bean名稱 --> <property name="target"> <ref local="serviceManager" /> </property> <!-- 說明攔截器bean名稱 --> <property name="interceptorNames"> <list> <value>cachePointCut</value> <value>cachePointCutAdvice</value> </list> </property> </bean> </beans>
各個bean的內容都作了凝視說明,值得注意的是,不要忘了代理工廠bean。
ehcache.xml中存儲Ehcache緩存配置的詳細信息,內容例如如下:
<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd">
<!-- 緩存文件位置 -->
<diskStore path="D:\\temp\\cache" />
<defaultCache maxElementsInMemory="1000" eternal="false" timeToIdleSeconds="120" timeToLiveSeconds="120" overflowToDisk="true" />
<!-- 定義緩存文件信息,當中「com.tt」爲緩存文件的名字 -->
<cache name="com.tt" maxElementsInMemory="10000" eternal="false" timeToIdleSeconds="300000" timeToLiveSeconds="600000" overflowToDisk="true" />
</ehcache>
能夠看到緩存的存儲的存儲位置設置爲「D:\temp\cache」。緩存名稱設置成了「com.tt」,如圖:
所需的java庫,詳見開頭的系統結構圖片,此處略。
運行測試類,測試結果例如如下:
經過運行結果咱們能夠看出:
第一次查找被攔截後發現是首次攔截,尚未緩存Cache。因此先運行一下原有接口類。獲得要查詢的數據,有多是經過數據庫查詢獲得的,而後再生成Cache,並將查詢獲得的數據放入Cache。
第二次查找被攔截後發現已經存在Cache。因而再也不運行原有接口類,也就是再也不查詢數據庫啦,直接經過Cache獲得查詢數據。固然這裏僅僅是簡單打印一下。
而後是第一次更新,被攔截後所作的操做是將Cache中的數據全部存入數據庫,並將Cache刪除。
最後是第三次查詢。被攔截後又發現系統不存在Cache。因而運行原接口類查詢數據庫,建立Cache。並將新查詢獲得的數據放入Cache。同第一次查詢的方式是同樣的。
至此咱們就實現了Spring搭配Ehcache所需要完畢的操做。
附件源碼能夠從個人github站點上獲取。