zoukankan      html  css  js  c++  java
  • spring aop ehcache

    引用:http://tom-duan.iteye.com/blog/99721

    需要使用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代码  收藏代码
    1. package com.co.cache.ehcache;  
    2.   
    3. import java.io.Serializable;  
    4.   
    5. import net.sf.ehcache.Cache;  
    6. import net.sf.ehcache.Element;  
    7.   
    8. import org.aopalliance.intercept.MethodInterceptor;  
    9. import org.aopalliance.intercept.MethodInvocation;  
    10. import org.apache.commons.logging.Log;  
    11. import org.apache.commons.logging.LogFactory;  
    12. import org.springframework.beans.factory.InitializingBean;  
    13. import org.springframework.util.Assert;  
    14.   
    15. public class MethodCacheInterceptor implements MethodInterceptor, InitializingBean  
    16. {  
    17.     private static final Log logger = LogFactory.getLog(MethodCacheInterceptor.class);  
    18.   
    19.     private Cache cache;  
    20.   
    21.     public void setCache(Cache cache) {  
    22.         this.cache = cache;  
    23.     }  
    24.   
    25.     public MethodCacheInterceptor() {  
    26.         super();  
    27.     }  
    28.   
    29.     /** 
    30.      * 拦截Service/DAO的方法,并查找该结果是否存在,如果存在就返回cache中的值, 
    31.      * 否则,返回数据库查询结果,并将查询结果放入cache 
    32.      */  
    33.     public Object invoke(MethodInvocation invocation) throws Throwable {  
    34.         String targetName = invocation.getThis().getClass().getName();  
    35.         String methodName = invocation.getMethod().getName();  
    36.         Object[] arguments = invocation.getArguments();  
    37.         Object result;  
    38.       
    39.         logger.debug("Find object from cache is " + cache.getName());  
    40.           
    41.         String cacheKey = getCacheKey(targetName, methodName, arguments);  
    42.         Element element = cache.get(cacheKey);  
    43.   
    44.         if (element == null) {  
    45.             logger.debug("Hold up method , Get method result and create cache........!");  
    46.             result = invocation.proceed();  
    47.             element = new Element(cacheKey, (Serializable) result);  
    48.             cache.put(element);  
    49.         }  
    50.         return element.getValue();  
    51.     }  
    52.   
    53.     /** 
    54.      * 获得cache key的方法,cache key是Cache中一个Element的唯一标识 
    55.      * cache key包括 包名+类名+方法名,如com.co.cache.service.UserServiceImpl.getAllUser 
    56.      */  
    57.     private String getCacheKey(String targetName, String methodName, Object[] arguments) {  
    58.         StringBuffer sb = new StringBuffer();  
    59.         sb.append(targetName).append(".").append(methodName);  
    60.         if ((arguments != null) && (arguments.length != 0)) {  
    61.             for (int i = 0; i < arguments.length; i++) {  
    62.                 sb.append(".").append(arguments[i]);  
    63.             }  
    64.         }  
    65.         return sb.toString();  
    66.     }  
    67.       
    68.     /** 
    69.      * implement InitializingBean,检查cache是否为空 
    70.      */  
    71.     public void afterPropertiesSet() throws Exception {  
    72.         Assert.notNull(cache, "Need a cache. Please use setCache(Cache) create it.");  
    73.     }  
    74.   
    75. }  



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

    Java代码  收藏代码
    1. Element element = cache.get(cacheKey);  


    这句代码的作用是获取cache中的element,如果cacheKey所对应的element不存在,将会返回一个null值 

    Java代码  收藏代码
    1. result = invocation.proceed();  


    这句代码的作用是获取所拦截方法的返回值,详细请查阅AOP相关文档。 

    随后,再建立一个拦截器MethodCacheAfterAdvice,作用是在用户进行create/update/delete操作时来刷新/remove相关cache内容,这个拦截器实现了AfterReturningAdvice接口,将会在所拦截的方法执行后执行在public void afterReturning(Object arg0, Method arg1, Object[] arg2, Object arg3)方法中所预定的操作 

    Java代码  收藏代码
    1. package com.co.cache.ehcache;  
    2.   
    3. import java.lang.reflect.Method;  
    4. import java.util.List;  
    5.   
    6. import net.sf.ehcache.Cache;  
    7.   
    8. import org.apache.commons.logging.Log;  
    9. import org.apache.commons.logging.LogFactory;  
    10. import org.springframework.aop.AfterReturningAdvice;  
    11. import org.springframework.beans.factory.InitializingBean;  
    12. import org.springframework.util.Assert;  
    13.   
    14. public class MethodCacheAfterAdvice implements AfterReturningAdvice, InitializingBean  
    15. {  
    16.     private static final Log logger = LogFactory.getLog(MethodCacheAfterAdvice.class);  
    17.   
    18.     private Cache cache;  
    19.   
    20.     public void setCache(Cache cache) {  
    21.         this.cache = cache;  
    22.     }  
    23.   
    24.     public MethodCacheAfterAdvice() {  
    25.         super();  
    26.     }  
    27.   
    28.     public void afterReturning(Object arg0, Method arg1, Object[] arg2, Object arg3) throws Throwable {  
    29.         String className = arg3.getClass().getName();  
    30.         List list = cache.getKeys();  
    31.         for(int i = 0;i<list.size();i++){  
    32.             String cacheKey = String.valueOf(list.get(i));  
    33.             if(cacheKey.startsWith(className)){  
    34.                 cache.remove(cacheKey);  
    35.                 logger.debug("remove cache " + cacheKey);  
    36.             }  
    37.         }  
    38.     }  
    39.   
    40.     public void afterPropertiesSet() throws Exception {  
    41.         Assert.notNull(cache, "Need a cache. Please use setCache(Cache) create it.");  
    42.     }  
    43.   
    44. }  


    上面的代码很简单,实现了afterReturning方法实现自AfterReturningAdvice接口,方法中所定义的内容将会在目标方法执行后执行,在该方法中

    Java代码  收藏代码
    1. String className = arg3.getClass().getName();  

    的作用是获取目标class的全名,如:com.co.cache.test.TestServiceImpl,然后循环cache的key list,remove cache中所有和该class相关的element。 

    随后,开始配置ehCache的属性,ehCache需要一个xml文件来设置ehCache相关的一些属性,如最大缓存数量、cache刷新的时间等等. 
    ehcache.xml 

    Java代码  收藏代码
    1. <ehcache>  
    2.     <diskStore path="c:\\myapp\\cache"/>  
    3.     <defaultCache  
    4.         maxElementsInMemory="1000"  
    5.         eternal="false"  
    6.         timeToIdleSeconds="120"  
    7.         timeToLiveSeconds="120"  
    8.         overflowToDisk="true"  
    9.         />  
    10.   <cache name="DEFAULT_CACHE"  
    11.         maxElementsInMemory="10000"  
    12.         eternal="false"  
    13.         timeToIdleSeconds="300000"  
    14.         timeToLiveSeconds="600000"  
    15.         overflowToDisk="true"  
    16.         />  
    17. </ehcache>  


    配置每一项的详细作用不再详细解释,有兴趣的请google下 ,这里需要注意一点defaultCache标签定义了一个默认的Cache,这个Cache是不能删除的,否则会抛出No default cache is configured异常。另外,由于使用拦截器来刷新Cache内容,因此在定义cache生命周期时可以定义较大的数值,timeToIdleSeconds="300000" timeToLiveSeconds="600000",好像还不够大? 

    然后,在将Cache和两个拦截器配置到Spring,这里没有使用2.0里面AOP的标签。 
    cacheContext.xml 

    Java代码  收藏代码
    1. <?xml version="1.0" encoding="UTF-8"?>  
    2. <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">  
    3. <beans>  
    4.     <!-- 引用ehCache的配置 -->  
    5.     <bean id="defaultCacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">  
    6.       <property name="configLocation">  
    7.         <value>ehcache.xml</value>  
    8.       </property>  
    9.     </bean>  
    10.       
    11.     <!-- 定义ehCache的工厂,并设置所使用的Cache name -->  
    12.     <bean id="ehCache" class="org.springframework.cache.ehcache.EhCacheFactoryBean">  
    13.       <property name="cacheManager">  
    14.         <ref local="defaultCacheManager"/>  
    15.       </property>  
    16.       <property name="cacheName">  
    17.           <value>DEFAULT_CACHE</value>  
    18.       </property>  
    19.     </bean>  
    20.   
    21.     <!-- find/create cache拦截器 -->  
    22.     <bean id="methodCacheInterceptor" class="com.co.cache.ehcache.MethodCacheInterceptor">  
    23.       <property name="cache">  
    24.         <ref local="ehCache" />  
    25.       </property>  
    26.     </bean>  
    27.     <!-- flush cache拦截器 -->  
    28.     <bean id="methodCacheAfterAdvice" class="com.co.cache.ehcache.MethodCacheAfterAdvice">  
    29.       <property name="cache">  
    30.         <ref local="ehCache" />  
    31.       </property>  
    32.     </bean>  
    33.       
    34.     <bean id="methodCachePointCut" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">  
    35.       <property name="advice">  
    36.         <ref local="methodCacheInterceptor"/>  
    37.       </property>  
    38.       <property name="patterns">  
    39.         <list>  
    40.             <value>.*find.*</value>  
    41.             <value>.*get.*</value>  
    42.         </list>  
    43.       </property>  
    44.     </bean>  
    45.     <bean id="methodCachePointCutAdvice" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">  
    46.       <property name="advice">  
    47.         <ref local="methodCacheAfterAdvice"/>  
    48.       </property>  
    49.       <property name="patterns">  
    50.         <list>  
    51.           <value>.*create.*</value>  
    52.           <value>.*update.*</value>  
    53.           <value>.*delete.*</value>  
    54.         </list>  
    55.       </property>  
    56.     </bean>  
    57. </beans>  


    上面的代码最终创建了两个"切入点",methodCachePointCut和methodCachePointCutAdvice,分别用于拦截不同方法名的方法,可以根据需要任意增加所需要拦截方法的名称。 
    需要注意的是 

    Java代码  收藏代码
    1. <bean id="ehCache" class="org.springframework.cache.ehcache.EhCacheFactoryBean">  
    2.       <property name="cacheManager">  
    3.         <ref local="defaultCacheManager"/>  
    4.       </property>  
    5.       <property name="cacheName">  
    6.           <value>DEFAULT_CACHE</value>  
    7.       </property>  
    8.     </bean>  


    如果cacheName属性内设置的name在ehCache.xml中无法找到,那么将使用默认的cache(defaultCache标签定义). 

    事实上到了这里,一个简单的Spring + ehCache Framework基本完成了,为了测试效果,举一个实际应用的例子,定义一个TestService和它的实现类TestServiceImpl,里面包含 

    两个方法getAllObject()和updateObject(Object Object),具体代码如下 
    TestService.java 

    Java代码  收藏代码
    1. package com.co.cache.test;  
    2.   
    3. import java.util.List;  
    4.   
    5. public interface TestService {  
    6.     public List getAllObject();  
    7.   
    8.     public void updateObject(Object Object);  
    9. }  



    TestServiceImpl.java 

    Java代码  收藏代码
    1. package com.co.cache.test;  
    2.   
    3. import java.util.List;  
    4.   
    5. public class TestServiceImpl implements TestService  
    6. {  
    7.     public List getAllObject() {  
    8.         System.out.println("---TestService:Cache内不存在该element,查找并放入Cache!");  
    9.         return null;  
    10.     }  
    11.   
    12.     public void updateObject(Object Object) {  
    13.         System.out.println("---TestService:更新了对象,这个Class产生的cache都将被remove!");  
    14.     }  
    15. }  


    使用Spring提供的AOP进行配置 
    applicationContext.xml 

    Java代码  收藏代码
    1. <?xml version="1.0" encoding="UTF-8"?>  
    2. <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">  
    3.   
    4. <beans>  
    5.     <import resource="cacheContext.xml"/>  
    6.       
    7.     <bean id="testServiceTarget" class="com.co.cache.test.TestServiceImpl"/>  
    8.       
    9.     <bean id="testService" class="org.springframework.aop.framework.ProxyFactoryBean">  
    10.       <property name="target">  
    11.           <ref local="testServiceTarget"/>  
    12.       </property>  
    13.       <property name="interceptorNames">  
    14.         <list>  
    15.           <value>methodCachePointCut</value>  
    16.           <value>methodCachePointCutAdvice</value>  
    17.         </list>  
    18.       </property>  
    19.     </bean>  
    20. </beans>  


    这里一定不能忘记import cacheContext.xml文件,不然定义的两个拦截器就没办法使用了。 

    最后,写一个测试的代码 
    MainTest.java 

    Java代码  收藏代码
    1. package com.co.cache.test;  
    2.   
    3. import org.springframework.context.ApplicationContext;  
    4. import org.springframework.context.support.ClassPathXmlApplicationContext;  
    5.   
    6. public class MainTest{  
    7.     public static void main(String args[]){  
    8.         String DEFAULT_CONTEXT_FILE = "/applicationContext.xml";  
    9.         ApplicationContext context =  new ClassPathXmlApplicationContext(DEFAULT_CONTEXT_FILE);  
    10.         TestService testService = (TestService)context.getBean("testService");  
    11.   
    12.         System.out.println("1--第一次查找并创建cache");  
    13.         testService.getAllObject();  
    14.           
    15.         System.out.println("2--在cache中查找");  
    16.         testService.getAllObject();  
    17.           
    18.         System.out.println("3--remove cache");  
    19.         testService.updateObject(null);  
    20.           
    21.         System.out.println("4--需要重新查找并创建cache");  
    22.         testService.getAllObject();  
    23.     }     
    24. }  



    运行,结果如下 

    Java代码  收藏代码
    1. 1--第一次查找并创建cache  
    2. ---TestService:Cache内不存在该element,查找并放入Cache!  
    3. 2--在cache中查找  
    4. 3--remove cache  
    5. ---TestService:更新了对象,这个Class产生的cache都将被remove!  
    6. 4--需要重新查找并创建cache  
    7. ---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,有兴趣的可以研究一下。

  • 相关阅读:
    Mongo简单查询总结
    将对象转换成Dictionary 字典
    C#调用NPOI组件导出Excel表格
    Lambda中的一些方法的总结
    LinQ总结
    简单的爬虫 一
    Python 中的注释规范
    在VM上配置一个能上网的网络设置
    Python 中新式类的内置方法
    Python 中的locals()
  • 原文地址:https://www.cnblogs.com/sode/p/2818456.html
Copyright © 2011-2022 走看看