Ehcache學習 轉

          http://ligf06.iteye.com/blog/1710887

          5.     Spring 中運用 EHCache

          需要使用 Spring 來實現一個 Cache 簡單的解決方案,具體需求如下:使用任意一個現有開源 Cache Framework,要求使用 Cache 系統中 Service 或則 DAO 層的 get/find 等方法返回結果,如果數據更新(使用 Create/update/delete 方法),則刷新 cache 中相應的內容。根據需求,計劃使用 Spring AOP + ehCache 來實現這個功能,采用 ehCache 原因之一是Spring 提供了 ehCache 的支持,至于為何僅僅支持 ehCache 而不支持 osCache 和JBossCache 無從得知(Hibernate???),但畢竟 Spring 提供了支持,可以減少一部分工作量:)。二是后來實現了 OSCache 和 JBoss Cache 的方式后,經過簡單測試發現幾個 Cache 在效率上沒有太大的區別(不考慮集群),決定采用 ehCahce。AOP 嘛,少不了攔截器,先創建一個實現了 MethodInterceptor 接口的攔截器,用來攔截Service/DAO 的方法調用,攔截到方法后,搜索該方法的結果在 cache 中是否存在,如果存在,返回 cache 中的緩存結果,如果不存在,返回查詢數據庫的結果,并將結果緩存到 cache 中。

          MethodCacheInterceptor.java

          Java 代碼

          package com.co.cache.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.apache.commons.logging.Log;

          import org.apache.commons.logging.LogFactory;

          import org.springframework.beans.factory.InitializingBean;

          import org.springframework.util.Assert;

          public class MethodCacheInterceptor implements MethodInterceptor, InitializingBean

          {

          private static final Log logger = LogFactory.getLog(MethodCacheInterceptor.class);

          private Cache cache;

          public void setCache(Cache cache) {

          this.cache = cache;

          }

          public MethodCacheInterceptor() {

          super();

          }

          /**

          攔截 Service/DAO 的方法,并查找該結果是否存在,如果存在就返回 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;

          logger.debug("Find object from cache is " + cache.getName());

          String cacheKey = getCacheKey(targetName, methodName, arguments);

          Element element = cache.get(cacheKey);

          if (element == null) {

          logger.debug("Hold up method , Get method result and create cache........!");

          result = invocation.proceed();

          element = new Element(cacheKey, (Serializable) result);

          cache.put(element);

          }

          return element.getValue();

          }

          /**

          獲得 cache key 的方法, cache key  Cache 中一個 Element 的唯一標識

          * cache key 包括 包名 + 類名 + 方法名,如 com.co.cache.service.UserServiceImpl.getAllUser

          */

          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 是否為空

          */

          public void afterPropertiesSet() throws Exception {

          Assert.notNull(cache, "Need a cache. Please use setCache(Cache) create it.");

          }

          }

          上面的代碼中可以看到,在方法 public Object invoke(MethodInvocation invocation) 中,完成了搜索Cache/ 新建 cache 的功能。

          Element element = cache.get(cacheKey);

          這句代碼的作用是獲取 cache 中的 element ,如果 cacheKey 所對應的 element 不存在,將會返回一個 null 值。

          Java 代碼

          result = invocation.proceed();

          這句代碼的作用是獲取所攔截方法的返回值,詳細請查閱 AOP 相關文檔。隨后,再建立一個攔截器MethodCacheAfterAdvice ,作用是在用戶進行 create/update/delete 操作時來刷新 /remove 相關cache 內容,這個攔截器實現了 AfterReturningAdvice 接口,將會在所攔截的方法執行后執行在 public void afterReturning(Object arg0, Method arg1,Object[] arg2, Object arg3) 方法中所預定的操作

          Java 代碼

          package com.co.cache.ehcache;

          import java.lang.reflect.Method;

          import java.util.List;

          import net.sf.ehcache.Cache;

          import org.apache.commons.logging.Log;

          import org.apache.commons.logging.LogFactory;

          import org.springframework.aop.AfterReturningAdvice;

          import org.springframework.beans.factory.InitializingBean;

          import org.springframework.util.Assert;

          public class MethodCacheAfterAdvice implements AfterReturningAdvice, InitializingBean

          {

          private static final Log logger = LogFactory.getLog(MethodCacheAfterAdvice.class);

          private Cache cache;

          public void setCache(Cache cache) {

          this.cache = cache;

          }

          public MethodCacheAfterAdvice() {

          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);

          logger.debug("remove cache " + cacheKey);

          }

          }

          }

          public void afterPropertiesSet() throws Exception {

          Assert.notNull(cache, "Need a cache. Please use setCache(Cache) create it.");

          }

          }

          上面的代碼很簡單,實現了 afterReturning 方法實現自 AfterReturningAdvice 接口,方法中所定義的內容將會在目標方法執行后執行,在該方法中的作用是獲取目標 class 的全名,如:com.co.cache.test.TestServiceImpl ,然后循環 cache  key list  remove cache 中所有和該 class 相關的 element 。

          Java 代碼

          String className = arg3.getClass().getName();

          隨后,開始配置 ehCache 的屬性, ehCache 需要一個 xml 文件來設置 ehCache 相關的一些屬性,如最大緩存數量、 cache 刷新的時間等等 .

          ehcache.xml

          <ehcache>

          <diskStore path="c:\\myapp\\cache"/>

          <defaultCache

          maxElementsInMemory="1000"

          eternal="false"

          timeToIdleSeconds="120"

          timeToLiveSeconds="120"

          overflowToDisk="true"

          />

          <cache name="DEFAULT_CACHE"

          maxElementsInMemory="10000"

          eternal="false"

          timeToIdleSeconds="300000"

          timeToLiveSeconds="600000"

          overflowToDisk="true"

          />

          </ehcache>

          配置每一項的詳細作用不再詳細解釋,有興趣的請 google 下 ,這里需要注意一點 defaultCache 標簽定義了一個默認的 Cache ,這個 Cache 是不能刪除的,否則會拋出 No default cache is configured異常。另外,由于使用攔截器來刷新 Cache 內容,因此在定義 cache 生命周期時可以定義較大的數值, timeToIdleSeconds="300000" , timeToLiveSeconds="600000" ,好像還不夠大?然后,在將Cache 和兩個攔截器配置到 Spring ,這里沒有使用 2.0 里面 AOP 的標簽。

          cacheContext.xml

          <?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 -->

          <bean id="ehCache" class="org.springframework.cache.ehcache.EhCacheFactoryBean">

          <property name="cacheManager">

          <ref local="defaultCacheManager"/>

          </property>

          <property name="cacheName">

          <value>DEFAULT_CACHE</value>

          </property>

          </bean>

          <!-- find/create cache 攔截器 -->

          <bean id="methodCacheInterceptor"

          class="com.co.cache.ehcache.MethodCacheInterceptor">

          <property name="cache">

          <ref local="ehCache" />

          </property>

          </bean>

          <!-- flush cache 攔截器 -->

          <bean id="methodCacheAfterAdvice"

          class="com.co.cache.ehcache.MethodCacheAfterAdvice">

          <property name="cache">

          <ref local="ehCache" />

          </property>

          </bean>

          <bean id="methodCachePointCut"

          class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">

          <property name="advice">

          <ref local="methodCacheInterceptor"/>

          </property>

          <property name="patterns">

          <list>

          <value>.*find.*</value>

          <value>.*get.*</value>

          </list>

          </property>

          </bean>

          <bean id="methodCachePointCutAdvice"

          class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">

          <property name="advice">

          <ref local="methodCacheAfterAdvice"/>

          </property>

          <property name="patterns">

          <list>

          <value>.*create.*</value>

          <value>.*update.*</value>

          <value>.*delete.*</value>

          </list>

          </property>

          </bean>

          </beans>

          上面的代碼最終創建了兩個 " 切入點 " , methodCachePointCut  methodCachePointCutAdvice ,分別用于攔截不同方法名的方法,可以根據需要任意增加所需要攔截方法的名稱。需要注意的是 Java代碼

          <bean id="ehCache" class="org.springframework.cache.ehcache.EhCacheFactoryBean">

          <property name="cacheManager">

          <ref local="defaultCacheManager"/>

          </property>

          <property name="cacheName">

          <value>DEFAULT_CACHE</value>

          </property>

          </bean>

          如果 cacheName 屬性內設置的 name  ehCache.xml 中無法找到,那么將使用默認的

          cache(defaultCache 標簽定義 ) ,事實上到了這里,一個簡單的 Spring + ehCache Framework 基本完成了,為了測試效果,舉一個實際應用的例子,定義一個 TestService 和它的實現類 TestServiceImpl ,里面包含兩個方法 getAllObject()  updateObject(Object Object) ,具體代碼如下:

          TestService.java

          Java 代碼

          package com.co.cache.test;

          import java.util.List;

          public interface TestService {

          public List getAllObject();

          public void updateObject(Object Object);

          }

          TestServiceImpl.java

          Java 代碼

          package com.co.cache.test;

          import java.util.List;

          public class TestServiceImpl implements TestService

          {

          public List getAllObject() {

          System.out.println("---TestService  Cache 內不存在該 element ,查找并放入 Cache ! ");

          return null;

          }

          public void updateObject(Object Object) {

          System.out.println("---TestService :更新了對象,這個 Class 產生的 cache 都將被 remove 

          ");

          }

          }

          使用 Spring 提供的 AOP 進行配置

          applicationContext.xml

          XML/HTML 代碼

          <?xml version="1.0" encoding="UTF-8"?>

          <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"

          "http://www.springframework.org/dtd/spring-beans.dtd ">

          <beans>

          <import resource="cacheContext.xml"/>

          <bean id="testServiceTarget" class="com.co.cache.test.TestServiceImpl"/>

          <bean id="testService" class="org.springframework.aop.framework.ProxyFactoryBean">

          <property name="target">

          <ref local="testServiceTarget"/>

          </property>

          <property name="interceptorNames">

          <list>

          <value>methodCachePointCut</value>

          <value>methodCachePointCutAdvice</value>

          </list>

          </property>

          </bean>

          </beans>

          這里一定不能忘記 import cacheContext.xml 文件,不然定義的兩個攔截器就沒辦法使用

          了。

          最后,寫一個測試的代碼

          MainTest.java

          Java 代碼

          (DEFAULT_CONTEXT_FILE);

          TestService testService = (TestService)context.getBean("testService");

          System.out.println("1-- 第一次查找并創建 cache");

          testService.getAllObject();

          System.out.println("2--  cache 中查找 ");

          testService.getAllObject();

          System.out.println("3--remove cache");

          testService.updateObject(null);

          System.out.println("4-- 需要重新查找并創建 cache");

          testService.getAllObject();

          }

          }

          運行,結果如下

          Java 代碼

          1-- 第一次查找并創建 cache

          ---TestService  Cache 內不存在該 element ,查找并放入 Cache !

          2--  cache 中查找

          3--remove cache

          ---TestService :更新了對象,這個 Class 產生的 cache 都將被 remove 

          4-- 需要重新查找并創建 cache

          ---TestService  Cache 內不存在該 element ,查找并放入 Cache 

          大功告成 . 可以看到,第一步執行 getAllObject() ,執行 TestServiceImpl 內的方法,并創建了 cache,在第二次執行 getAllObject() 方法時,由于 cache 有該方法的緩存,直接從 cache  get 出方法的結果,所以沒有打印出 TestServiceImpl 中的內容,而第三步,調用了 updateObject 方法,和TestServiceImpl 相關的 cache  remove ,所以在第四步執行時,又執行 TestServiceImpl 中的方法,創建 Cache 。網上也有不少類似的例子,但是很多都不是很完備,自己參考了一些例子的代碼,其實在 spring-modules 中也提供了對幾種 cache 的支持, ehCache , OSCache , JBossCache 這些,看了一下,基本上都是采用類似的方式,只不過封裝的更完善一些,主要思路也還是 Spring  AOP ,有興趣的可以研究一下。


          posted on 2016-06-18 15:45 youngturk 閱讀(148) 評論(0)  編輯  收藏 所屬分類: 筆試題

          <2016年6月>
          2930311234
          567891011
          12131415161718
          19202122232425
          262728293012
          3456789

          導航

          統計

          公告

          this year :
          1 jQuery
          2 freemarker
          3 框架結構
          4 口語英語

          常用鏈接

          留言簿(6)

          隨筆分類

          隨筆檔案

          文章分類

          文章檔案

          相冊

          EJB學習

          Flex學習

          learn English

          oracle

          spring MVC web service

          SQL

          Struts

          生活保健

          解析文件

          搜索

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 靖江市| 上杭县| 麟游县| 西盟| 师宗县| 紫金县| 武夷山市| 鄂尔多斯市| 北票市| 苍梧县| 科技| 鹿邑县| 天门市| 乌苏市| 阜南县| 洞头县| 嘉善县| 分宜县| 南川市| 和静县| 阿尔山市| 调兵山市| 肥西县| 怀来县| 盐源县| 合川市| 邛崃市| 闽侯县| 华池县| 仁化县| 聊城市| 深州市| 襄城县| 文水县| 东源县| 尼玛县| 绥棱县| 博兴县| 三台县| 靖西县| 定安县|