使用自定義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下的配置
<!-- ############## aspectj 4 ehcache ############# --> <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> <!-- 定義ehCache的工廠,並設置所使用的Cache name --> <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
@Target({ElementType.METHOD,ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface MethodCache { int second() default 0; }
<bean id = "methodCacheAspectJ">是一個aspectj進行Pointcuts和Advice的類需註入methodCache
@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){ // annotation沒有設second值則使用ehcache.xml中自定義值 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); }
使用自定義annotation接口進行aspectj動態緩存