二,动态缓存
方案1:使用自定义annotation接口进行aspectj动态缓存
 
由于系统需求需要对各个接口进行key-value缓存(以参数为key,返回的对象为value),当然对于这种情况首先考虑到的是使用aop,前段时间看过aspectj的一些介绍,借此机会正好加以应用和体会一下,aspectj是AOP最早成熟的java实现,它稍微扩展了一下java语言,增加了一些keyword等,具体的aspectj的基本语法见这里,进行缓存的框架使用较成熟的ehcache.
下面开始进行配置
首先是ehcache的配置文件
- <?xml version="1.0" encoding="UTF-8"?>  
- <ehcache>  
-     <diskStore path="/home/workspace/gzshine/trunk/ehcache"/>  
-     <cache name="DEFAULT_CACHE"  
-         maxElementsInMemory="10000"  
-         eternal="false"  
-         timeToIdleSeconds="3600"  
-         timeToLiveSeconds="3600"  
-         overflowToDisk="true"  
-         />  
- </ehcache>   
 
这个的DEFAULT_CACHE是默认配置,最大的缓存数为10000,时间为一个小时
接下来的是spring下的配置
- <?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:aop="http://www.springframework.org/schema/aop"  
-     xmlns:tx="http://www.springframework.org/schema/tx"  
-     xmlns:context="http://www.springframework.org/schema/context"  
-     xsi:schemaLocation="  
-        http://www.springframework.org/schema/beans  
-        http://www.springframework.org/schema/beans/spring-beans-2.5.xsd  
-        http://www.springframework.org/schema/tx  
-        http://www.springframework.org/schema/tx/spring-tx-2.5.xsd  
-        http://www.springframework.org/schema/aop  
-        http://www.springframework.org/schema/aop/spring-aop-2.5.xsd  
-        http://www.springframework.org/schema/context  
-        http://www.springframework.org/schema/context/spring-context-2.5.xsd">  
-   
-   
-       
-     <aop:aspectj-autoproxy proxy-target-class="true"/>  
-     <bean id = "methodCacheAspectJ" class="com.***.shine.aspectj.MethodCacheAspectJ" >  
-         <property name="cache">  
-             <ref local="methodCache" />  
-         </property>  
-     </bean>  
-       
-     <bean id="cacheManager"  
-         class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">  
-         <property name="configLocation">  
-             <value>classpath:ehcache.xml</value>  
-         </property>  
-     </bean>  
-       
-       
-       
-     <bean id="methodCache"  
-         class="org.springframework.cache.ehcache.EhCacheFactoryBean">  
-         <property name="cacheManager">  
-             <ref local="cacheManager" />  
-         </property>  
-         <property name="cacheName">  
-             <value>DEFAULT_CACHE</value>  
-         </property>  
-     </bean>  
 
<aop:aspectj-autoproxy proxy-target-class="true"/>
是为aspectj在所有class下开启自动动态代理
<bean id="cacheManager">指定刚刚的ehcache配置文件
接下来编写一个自定义的annotation
- package com.***.shine.cache;  
-   
- import java.lang.annotation.Documented;  
- import java.lang.annotation.ElementType;  
- import java.lang.annotation.Retention;  
- import java.lang.annotation.RetentionPolicy;  
- import java.lang.annotation.Target;  
-   
- @Target({ElementType.METHOD,ElementType.TYPE})  
- @Retention(RetentionPolicy.RUNTIME)  
- @Documented  
- public @interface MethodCache {  
-     int second() default 0;   
- }  
 
<bean id = "methodCacheAspectJ">是一个aspectj进行Pointcuts和Advice的类需注入methodCache
- package com.***.shine.aspectj;  
-   
- @Aspect  
- public class MethodCacheAspectJ {  
-     Log logger = LogFactory.getLog(MethodCacheAspectJ.class);  
-       
-     private Cache cache;  
-       
-      
-  
-   
-     public void setCache(Cache cache) {  
-         this.cache = cache;  
-     }   
-       
-     @Pointcut("@annotation(com.***.shine.cache.MethodCache)")  
-     public void methodCachePointcut(){    
-     }  
-       
-     @Around("methodCachePointcut()")  
-     public Object methodCacheHold(ProceedingJoinPoint joinPoint) throws Throwable{  
-         String targetName = joinPoint.getTarget().getClass().getName();  
-         String methodName = joinPoint.getSignature().getName();  
-         Object[] arguments = joinPoint.getArgs();  
-         Object result = null;  
-         String cacheKey = getCacheKey(targetName, methodName, arguments);  
-         Element element = cache.get(cacheKey);  
-         if (element == null) {  
-             try{  
-                 result = joinPoint.proceed();  
-             }catch(Exception e){  
-                 logger.info(e);  
-             }  
-             if(result!=null){  
-                 try{  
-                     element = new Element(cacheKey, (Serializable) result);  
-                     Class targetClass = Class.forName(targetName);  
-                     Method[] method = targetClass.getMethods();  
-                     int second = 0;  
-                     for(Method m:method){  
-                         if (m.getName().equals(methodName)) {  
-                             Class[] tmpCs = m.getParameterTypes();  
-                             if(tmpCs.length==arguments.length){  
-                                 MethodCache methodCache = m.getAnnotation(MethodCache.class);  
-                                 second = methodCache.second();  
-                                 break;  
-                             }  
-                         }  
-                     }  
-                     if(second>0){   
-                         element.setTimeToIdle(second);  
-                         element.setTimeToLive(second);  
-                     }  
-                     cache.put(element);  
-                 }catch(Exception e){  
-                     logger.info("!!!!!!!!!"+cacheKey+"!!!!!!!!!未能执行方法缓存"+e);  
-                 }  
-             }  
-         }  
-         return element.getValue();  
-     }  
-   
-      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++) {  
-                 if (arguments[i] instanceof Date) {  
-                     sb.append(".").append(  
-                             DateUtil.datetoString((Date) arguments[i]));  
-                 } else {  
-                     sb.append(".").append(arguments[i]);  
-                 }  
-             }  
-         }  
-         return sb.toString();  
-     }  
- }  
 
@Pointcut("@annotation(com.netease.shine.cache.MethodCache)")
对有应用com.netease.shine.cache.MethodCache进行注解的方法进行横切面拦截
@Around("methodCachePointcut()")
并在Advice中处理这个Pointcut,这里的的Advice使用的是Around(环绕通知)
String cacheKey = getCacheKey(targetName, methodName, arguments);
接下来使用类型,方法名,参数为key进入缓存处理
Element element = cache.get(cacheKey);
当然如果在cache队列中取得非null对象则直接返回该对象
MethodCache methodCache = m.getAnnotation(MethodCache.class);
second = methodCache.second();
取得second的值(缓存的时间,如在@annotation中无重写只为int second() default 0)
element.setTimeToIdle(second);
element.setTimeToLive(second);
如果非零则重新设置缓存时间
- @MethodCache(second=300)  
- public List<Sort> getSort(int type,int parentid){  
-     System.out.println("!!!!!!!!!!!!!没缓存到");  
-     Row row = new Row();  
-     row.put("type", type);  
-     row.put("parentid", parentid);  
-     return (List<Sort>)gz_Template.queryForList("sort.getSort", row);  
- }  
 
最后需要将@MethodCache要缓存方法的实现类
方案2 web应用的java动态缓存
 
可以实现不等待,线程自动更新缓存
 
 java动态缓存jar包请下载。
 
源代码:
 
| 001 | CacheData.java 存放缓存数据的Bean | 
 
| 006 | packagecom.cari.web.cache; | 
 
| 012 | publicclassCacheData { | 
 
| 021 |     publicCacheData(Object data, longtime, intcount) { | 
 
| 027 |     publicCacheData(Object data) { | 
 
| 029 |         this.time = System.currentTimeMillis(); | 
 
| 033 |     publicvoidaddCount() { | 
 
| 037 |     publicintgetCount() { | 
 
| 040 |     publicvoidsetCount(intcount) { | 
 
| 043 |     publicObject getData() { | 
 
| 046 |     publicvoidsetData(Object data) { | 
 
| 049 |     publiclonggetTime() { | 
 
| 052 |     publicvoidsetTime(longtime) { | 
 
| 059 | CacheOperation.java 缓存处理类 | 
 
| 061 | packagecom.cari.web.cache; | 
 
| 063 | importjava.lang.reflect.Method; | 
 
| 064 | importjava.util.ArrayList; | 
 
| 065 | importjava.util.Arrays; | 
 
| 066 | importjava.util.Hashtable; | 
 
| 068 | importorg.apache.commons.logging.Log; | 
 
| 069 | importorg.apache.commons.logging.LogFactory; | 
 
| 074 | publicclassCacheOperation { | 
 
| 075 |     privatestaticfinalLog log = LogFactory.getLog(CacheOperation.class); | 
 
| 076 |     privatestaticCacheOperation singleton = null; | 
 
| 078 |     privateHashtable cacheMap; | 
 
| 080 |     privateArrayList threadKeys; | 
 
| 082 |     publicstaticCacheOperation getInstance() { | 
 
| 083 |         if(singleton == null) { | 
 
| 084 |             singleton = newCacheOperation(); | 
 
| 089 |     privateCacheOperation() { | 
 
| 090 |         cacheMap = newHashtable(); | 
 
| 091 |         threadKeys = newArrayList(); | 
 
| 096 |      * 与方法getCacheData(String key, long intervalTime, int maxVisitCount)配合使用 | 
 
| 100 |     publicvoidaddCacheData(String key, Object data) { | 
 
| 101 |         addCacheData(key, data, true); | 
 
| 104 |     privatevoidaddCacheData(String key, Object data, booleancheck) { | 
 
| 105 |         if(Runtime.getRuntime().freeMemory() < 5L*1024L*1024L) { | 
 
| 106 |             log.warn("WEB缓存:内存不足,开始清空缓存!"); | 
 
| 107 |             removeAllCacheData(); | 
 
| 109 |         } elseif(check && cacheMap.containsKey(key)) { | 
 
| 110 |             log.warn("WEB缓存:key值= "+ key + " 在缓存中重复, 本次不缓存!"); | 
 
| 113 |         cacheMap.put(key, newCacheData(data)); | 
 
| 118 |      * 与方法addCacheData(String key, Object data)配合使用 | 
 
| 120 |      * @param intervalTime 缓存的时间周期,小于等于0时不限制 | 
 
| 121 |      * @param maxVisitCount 访问累积次数,小于等于0时不限制 | 
 
| 124 |     publicObject getCacheData(String key, longintervalTime, intmaxVisitCount) { | 
 
| 125 |         CacheData cacheData = (CacheData)cacheMap.get(key); | 
 
| 126 |         if(cacheData == null) { | 
 
| 129 |         if(intervalTime > 0&& (System.currentTimeMillis() - cacheData.getTime()) > intervalTime) { | 
 
| 130 |             removeCacheData(key); | 
 
| 133 |         if(maxVisitCount > 0&& (maxVisitCount - cacheData.getCount()) <= 0) { | 
 
| 134 |             removeCacheData(key); | 
 
| 137 |             cacheData.addCount(); | 
 
| 139 |         returncacheData.getData(); | 
 
| 143 |      * 当缓存中数据失效时,用不给定的方法线程更新数据 | 
 
| 144 |      * @param o 取得数据的对像(该方法是静态方法是不用实例,则传Class实列) | 
 
| 145 |      * @param methodName 该对像中的方法 | 
 
| 146 |      * @param parameters 该方法的参数列表(参数列表中对像都要实现toString方法,若列表中某一参数为空则传它所属类的Class) | 
 
| 147 |      * @param intervalTime 缓存的时间周期,小于等于0时不限制 | 
 
| 148 |      * @param maxVisitCount 访问累积次数,小于等于0时不限制 | 
 
| 151 |     publicObject getCacheData(Object o, String methodName,Object[] parameters, | 
 
| 152 |             longintervalTime, intmaxVisitCount) { | 
 
| 153 |         Class oc = o instanceofClass ? (Class)o : o.getClass(); | 
 
| 154 |         StringBuffer key = newStringBuffer(oc.getName()); | 
 
| 155 |         key.append("-").append(methodName); | 
 
| 156 |         if(parameters != null) { | 
 
| 157 |             for(inti = 0; i < parameters.length; i++) { | 
 
| 158 |                 if(parameters[i] instanceofObject[]) { | 
 
| 159 |                     key.append("-").append(Arrays.toString((Object[])parameters[i])); | 
 
| 161 |                     key.append("-").append(parameters[i]); | 
 
| 166 |         CacheData cacheData = (CacheData)cacheMap.get(key.toString()); | 
 
| 167 |         if(cacheData == null) { | 
 
| 168 |             Object returnValue = invoke(o, methodName, parameters, key.toString()); | 
 
| 169 |             returnreturnValue instanceofClass ? null: returnValue; | 
 
| 171 |         if(intervalTime > 0&& (System.currentTimeMillis() - cacheData.getTime()) > intervalTime) { | 
 
| 172 |             daemonInvoke(o, methodName, parameters, key.toString()); | 
 
| 173 |         } elseif(maxVisitCount > 0&& (maxVisitCount - cacheData.getCount()) <= 0) { | 
 
| 174 |             daemonInvoke(o, methodName, parameters, key.toString()); | 
 
| 176 |             cacheData.addCount(); | 
 
| 178 |         returncacheData.getData(); | 
 
| 186 |      * @return 若反射调用方法返回值为空则返回该值的类型 | 
 
| 188 |     privateObject invoke(Object o, String methodName,Object[] parameters, String key) { | 
 
| 189 |         Object returnValue = null; | 
 
| 192 |             if(parameters != null) { | 
 
| 193 |                 pcs = newClass[parameters.length]; | 
 
| 194 |                 for(inti = 0; i < parameters.length; i++) { | 
 
| 195 |                     if(parameters[i] instanceofMethodInfo) { | 
 
| 196 |                         MethodInfo pmi = (MethodInfo)parameters[i]; | 
 
| 197 |                         Object pre = invoke(pmi.getO(), pmi.getMethodName(), pmi.getParameters(), null); | 
 
| 200 |                     if(parameters[i] instanceofClass) { | 
 
| 201 |                         pcs[i] = (Class)parameters[i]; | 
 
| 202 |                         parameters[i] = null; | 
 
| 204 |                         pcs[i] = parameters[i].getClass(); | 
 
| 208 |             Class oc = o instanceofClass ? (Class)o : o.getClass(); | 
 
| 210 |             Method m = matchMethod(oc, methodName, pcs); | 
 
| 211 |             returnValue = m.invoke(o, parameters); | 
 
| 212 |             if(key != null&& returnValue != null) { | 
 
| 213 |                 addCacheData(key, returnValue, false); | 
 
| 215 |             if(returnValue == null) { | 
 
| 216 |                 returnValue = m.getReturnType(); | 
 
| 218 |         } catch(Exception e) { | 
 
| 219 |             log.error("调用方法失败,methodName="+ methodName); | 
 
| 221 |                 removeCacheData(key); | 
 
| 222 |                 log.error("更新缓存失败,缓存key="+ key); | 
 
| 230 |      * 找不到完全匹配的方法时,对参数进行向父类匹配 | 
 
| 231 |      * 因为方法aa(java.util.List) 与 aa(java.util.ArrayList)不能自动匹配到 | 
 
| 237 |      * @throws NoSuchMethodException | 
 
| 238 |      * @throws NoSuchMethodException | 
 
| 240 |     privateMethod matchMethod(Class oc, String methodName, Class[] pcs | 
 
| 241 |             ) throwsNoSuchMethodException, SecurityException { | 
 
| 243 |             Method method = oc.getDeclaredMethod(methodName, pcs); | 
 
| 245 |         } catch(NoSuchMethodException e) { | 
 
| 246 |             Method[] ms = oc.getDeclaredMethods(); | 
 
| 247 |             aa:for(inti = 0; i < ms.length; i++) { | 
 
| 248 |                 if(ms[i].getName().equals(methodName)) { | 
 
| 249 |                     Class[] pts = ms[i].getParameterTypes(); | 
 
| 250 |                     if(pts.length == pcs.length) { | 
 
| 251 |                         for(intj = 0; j < pts.length; j++) { | 
 
| 252 |                             if(!pts[j].isAssignableFrom(pcs[j])) { | 
 
| 260 |             thrownewNoSuchMethodException(); | 
 
| 265 |      * 新启线程后台调用给定方法更新缓存中数据据 | 
 
| 271 |     privatevoiddaemonInvoke(Object o, String methodName,Object[] parameters, String key) { | 
 
| 272 |         if(!threadKeys.contains(key)) { | 
 
| 273 |             InvokeThread t = newInvokeThread(o, methodName, parameters, key); | 
 
| 279 |      * 些类存放方法的主调对像,名称及参数数组 | 
 
| 283 |     publicclassMethodInfo { | 
 
| 285 |         privateString methodName; | 
 
| 286 |         privateObject[] parameters; | 
 
| 287 |         publicMethodInfo(Object o, String methodName,Object[] parameters) { | 
 
| 289 |             this.methodName = methodName; | 
 
| 290 |             this.parameters = parameters; | 
 
| 292 |         publicString getMethodName() { | 
 
| 295 |         publicvoidsetMethodName(String methodName) { | 
 
| 296 |             this.methodName = methodName; | 
 
| 298 |         publicObject getO() { | 
 
| 301 |         publicvoidsetO(Object o) { | 
 
| 304 |         publicObject[] getParameters() { | 
 
| 307 |         publicvoidsetParameters(Object[] parameters) { | 
 
| 308 |             this.parameters = parameters; | 
 
| 311 |         publicString toString() { | 
 
| 312 |             StringBuffer str = newStringBuffer(methodName); | 
 
| 313 |             if(parameters != null) { | 
 
| 315 |                 for(inti = 0; i < parameters.length; i++) { | 
 
| 316 |                     if(parameters[i] instanceofObject[]) { | 
 
| 317 |                         str.append(Arrays.toString((Object[])parameters[i])).append(","); | 
 
| 319 |                         str.append(parameters[i]).append(","); | 
 
| 324 |             returnstr.toString(); | 
 
| 333 |     privateclassInvokeThread extendsThread { | 
 
| 335 |         privateString methodName; | 
 
| 336 |         privateObject[] parameters; | 
 
| 338 |         publicInvokeThread(Object o, String methodName,Object[] parameters, String key) { | 
 
| 340 |             this.methodName = methodName; | 
 
| 341 |             this.parameters = parameters; | 
 
| 347 |             invoke(o, methodName, parameters, key); | 
 
| 348 |             threadKeys.remove(key); | 
 
| 356 |     publicvoidremoveCacheData(String key) { | 
 
| 357 |         cacheMap.remove(key); | 
 
| 364 |     publicvoidremoveAllCacheData() { | 
 
| 368 |     publicString toString() { | 
 
| 369 |         StringBuffer sb = newStringBuffer("************************ "); | 
 
| 370 |         sb.append("正在更新的缓存数据: "); | 
 
| 371 |         for(inti = 0; i < threadKeys.size(); i++) { | 
 
| 372 |             sb.append(threadKeys.get(i)).append(" "); | 
 
| 374 |         sb.append("当前缓存大小:").append(cacheMap.size()).append(" "); | 
 
| 375 |         sb.append("************************"); | 
 
| 376 |         returnsb.toString(); | 
 
| 378 | }
     用法: 例1:代码片段如下: 
| 07 |   publicvoidgetData() { |  
| 09 |     DataCreator c = newDataCreator(); |  
| 11 |     String result = c.initUrlData(urlStr,encoding); |  
| 13 |     System.out.println(result); |      每次执行上面代码时都要通过调用 initUrlData方法取得数据,假设此方法很耗资源而耗时间,但对数据时实性要求不高,就是可以用以下方式进行缓存处理,保证很快地取得数据,并根据设置的参数自动更新缓存中数据 注意:initUrlData方法参数值一样时才属于同一个缓存,否则会生成一个新的缓存,也就是说从缓存中取数据与initUrlData方法参数值有关   
| 01 | publicvoidgetData() { |  
| 03 |     DataCreator data = newDataCreator(); |  
| 05 |     CacheOperation co = CacheOperation.getInstance(); |  
| 07 |     String str = (String)co.getCacheData(data, "initUrlData",newObject[]{urlStr, encoding},  120000, 100); |  
| 09 |     System.out.println(result); |      getCacheData方法返回值与initUrlData方法返回类型一样,参数说明: data:调用initUrlData方法的实列,如果该方法是静态的,则传类的类型,如(DataCreator .class); "initUrlData":方法名称; new Object[]{urlStr, encoding}:initUrlData方法的参数数组,如果某一参数为空则传该参数的类型,若encoding 为空,则为new Object[]{urlStr, String.class}或new Object[]{urlStr, ""}; 120000:缓存时间,单位:豪秒,即过两分钟更新一次缓存;值为0时为不限,即不更新缓存; 100:访问次数,当缓存中数据被访问100次时更新一次缓存;值为0时为不限,即不更新缓存; 例2:代码片段如下:   
| 1 | String province = request.getParameter("province"); |  
| 3 | String city= request.getParameter("city"); |  
| 5 | String county= request.getParameter("county"); |  
| 7 | Document doc = XMLBuilder.buildLatelyKeyword(kwm.latelyKeyword(province, city, county)); |        做缓存并两分钟更新一次,如下:   
| 01 | String province = request.getParameter("province"); |  
| 03 | String city= request.getParameter("city"); |  
| 05 | String county= request.getParameter("county"); |  
| 07 | CacheOperation co = CacheOperation.getInstance(); |  
| 09 | MethodInfo mi = co.newMethodInfo(kwm, "latelyKeyword", newObject[]{province, city, county}); |  
| 11 | Document doc = (Document )co.getCacheData(XMLBuilder.class,"buildLatelyKeyword",newObject[],120000, 0); |        以上方法是嵌套调用, 要先定义内部方法说明即MethodInfo,此类是CacheOperation 的一个内部类。 |