由于系統(tǒng)需求需
要對(duì)各個(gè)接口進(jìn)行key-value緩存(以參數(shù)為key,返回的對(duì)象為value),當(dāng)然對(duì)于這種情況首先考慮到的是使用aop,前段時(shí)間看過
aspectj的一些介紹,借此機(jī)會(huì)正好加以應(yīng)用和體會(huì)一下,aspectj是AOP最早成熟的java實(shí)現(xiàn),它稍微擴(kuò)展了一下java語(yǔ)言,增加了一些
keyword等,具體的aspectj的基本語(yǔ)法見[ur=http://today.java.net/pub/a/today/2003/12
/26/ch3AspectJSyntaxBasics.html]這里[/url],進(jìn)行緩存的框架使用較成熟的ehcache.
下面開始進(jìn)行配置
首先是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>
這個(gè)的DEFAULT_CACHE是默認(rèn)配置,最大的緩存數(shù)為10000,時(shí)間為一個(gè)小時(shí)
接下來(lái)的是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">
<!-- ############## 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的工廠,并設(shè)置所使用的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下開啟自動(dòng)動(dòng)態(tài)代理
<bean id="cacheManager">指定剛剛的ehcache配置文件
接下來(lái)編寫一個(gè)自定義的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">是一個(gè)aspectj進(jìn)行Pointcuts和Advice的類需注入methodCache
package com.***.shine.aspectj;
@Aspect
public class MethodCacheAspectJ {
Log logger = LogFactory.getLog(MethodCacheAspectJ.class);
private Cache cache;
/**
* 設(shè)置緩存名
*/
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沒有設(shè)second值則使用ehcache.xml中自定義值
element.setTimeToIdle(second);
element.setTimeToLive(second);
}
cache.put(element);
}catch(Exception e){
logger.info("!!!!!!!!!"+cacheKey+"!!!!!!!!!未能執(zhí)行方法緩存"+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)")
對(duì)有應(yīng)用com.netease.shine.cache.MethodCache進(jìn)行注解的方法進(jìn)行橫切面攔截
@Around("methodCachePointcut()")
并在Advice中處理這個(gè)Pointcut,這里的的Advice使用的是Around(環(huán)繞通知)
String cacheKey = getCacheKey(targetName, methodName, arguments);
接下來(lái)使用類型,方法名,參數(shù)為key進(jìn)入緩存處理
Element element = cache.get(cacheKey);
當(dāng)然如果在cache隊(duì)列中取得非null對(duì)象則直接返回該對(duì)象
MethodCache methodCache = m.getAnnotation(MethodCache.class);
second = methodCache.second();
取得second的值(緩存的時(shí)間,如在@annotation中無(wú)重寫只為int second() default 0)
element.setTimeToIdle(second);
element.setTimeToLive(second);
如果非零則重新設(shè)置緩存時(shí)間
@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);
}
----------------------------------------
陳于喆
Mail: chenyz@corp.netease.com