`
elicer
  • 浏览: 131418 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

用自定义 Annotation 改良 Aop cache 的实现

阅读更多
我想大家对于AOP的cache的实现都不陌生,老版本的AOP的cache的key一般是用className+methodName+parameters 拼成一个key,在这里paremeters最好都是String的,如果是一个对象型的在拼key时就会出现问题,可能我每次调某个方法时传进来的参数object都是new出来的,可能尽管他们的内容是一样的,但是他们的内存地址是不一样的,如果我们只是在StringBuffer中append一下的话,就有可能出现我调相同的方法,传相同的参数,但是拼出来的key却是不一样的,这就导致cache失效,而且每调一次cache中就会出现一份相同的返回数据,这里我想到了用自定义Annotation来解决这个问题。
通常情况下,我们cache的都是query的数据,一般都返回一个list,list中存放的是某个model.下面我以我的一个实例进行讲解,DB里有一张表Magazine,做的比较多的查询是根据价格跟售出数目,现在有个需求需要把这些DB中的magazine按每次查询的价格跟售出数目进行分组cache,
Magazine class如下
public class Magazine {
	private String isbn;
	
	private String title;
    @CacheKey(keySequence = 1)
	private BigDecimal price;
    @CacheKey(keySequence = 2)
	private int copySold;

	private int version;

	private String deleteFlag;

	private boolean displayFlag;

}



然后我把service方法就定义成List<Magezine> getMagazineList(Magazine magazine)每当调这个方法时就传入一个magazine里面只有price跟copySold字段有值,
这里我的做法是,自己写一个Annotatioan用来标注分组条件所用的field,这里就是price 跟copySlod.
Annotation如下
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.FIELD)   
@Retention(RetentionPolicy.RUNTIME) 
public @interface CacheKey {
   int keySequence();
}



这样在我的AOP的interceptor里,当我在拦截了这个方法后在ping cachekey时先取得标注了CacheKey的field然后通过反射把它们的值取出来拼成一个cachekey,这样不管你每次这个magazine是不是新new 出来的,只要条件相同拼出来的key肯定是一样的(例如查询价格是20快的售出400本的书的magazine的话,不管你每次传进来的magazine是不是同一个对象,只要prize=20,copySold=400,拼出来的key始终是一样的都是className+methodName+20+400)就这保证的cache的有效性,也防止了cache中有重复的数据。拦截器的代码如下,主要逻辑在getCacheKey()这个方法中:

public class AnnotationMethodCacheInterceptor implements MethodInterceptor {

	private Cache methodCache;

	public void setMethodCache(Cache methodCache) {
		this.methodCache = methodCache;
	}

	@SuppressWarnings("unchecked")
	public Object invoke(MethodInvocation invocation) throws Throwable {

		String targetName = invocation.getThis().getClass().getInterfaces()[0]
				.getName();
		String methodName = invocation.getMethod().getName();
		Object[] arguments = invocation.getArguments();
        Class[] cs = new Class[arguments.length];   
        for (int k = 0; k < arguments.length; k++) {   
            cs[k] = arguments[k].getClass();   
        }   
		if (invocation.getThis().getClass().getInterfaces()[0].isAnnotationPresent(
				EntityCache.class)) {
			EntityCache entityCache = (EntityCache)invocation.getThis().getClass().getInterfaces()[0].getAnnotation(EntityCache.class);   
			return getResult(targetName, methodName, arguments, invocation,entityCache.expireTime());
		} else {
			if (invocation.getMethod().isAnnotationPresent(MethodCache.class)) {
				MethodCache methodCache = invocation.getMethod().getAnnotation(MethodCache.class);
				return getResult(targetName, methodName, arguments, invocation,methodCache.expireTime());
		
			} else {
				return invocation.proceed();
			}
		}
	}

    private Object getResult(String targetName, String methodName, Object[] arguments,   
            MethodInvocation invocation, int expire) throws Throwable {   
        Object result;   
           
        String cacheKey = getCacheKey(targetName, methodName, arguments[0]);   
        Element element = methodCache.get(cacheKey);   
        if (element == null) {   
            synchronized (this) {   
                element = methodCache.get(cacheKey);   
                if (element == null) {   
                    result = invocation.proceed();   
       
                    element = new Element(cacheKey, (Serializable) result);   
                       
                    //annotation没有设expire值则使用ehcache.xml中自定义值   
                    if (expire > 0) {   
                        element.setTimeToIdle(expire);   
                        element.setTimeToLive(expire);   
                    }   
                    methodCache.put(element);   
                }   
            }   
        }   
           
        return element.getValue();   
    } 

    public String getCacheKey(String targetNM, String methodNM,Object object) { 
        StringBuilder datakey = new StringBuilder(); 
        dataKey.append(targeNM).append(methodNM);
        try { 

            // 取得 参数对象中的 field
            Field fields[] = object.getClass().getDeclaredFields(); 

            // 找到参数对象中作为key的field
            for (Field field : fields) { 

                // 如果此field家了cachekey的annotation
            	CacheKey sscCacheKey = 
                        field.getAnnotation(CacheKey.class); 
                if (sscCacheKey != null) { 

                    // 拼出此field的get方法
                    char[] charArray = field.getName().toCharArray(); 
                    charArray[0] = Character.toUpperCase(charArray[0]); 
                    String methodName = "get" + new String(charArray); 

                    // 通过调用此field的get 方法取到它的值
                    Method method = object.getClass().getMethod(methodName, new Class[0]); 
                    Object keyValue = method.invoke(object, new Object[0]); 

                    
                    if (keyValue != null) { 
                        if (keyValue instanceof java.util.Date) { 
                            SimpleDateFormat sdf = 
                                    new SimpleDateFormat("yyyy-MM-dd"); 
                            datakey.append(sdf.format((Date)keyValue)); 
                        } else { 
                            datakey.append(keyValue); 
                        } 
                    } 
                } 
            } 

        } catch(Exception e) {
        	e.printStackTrace();
        }
        return datakey.toString(); 
        
    } 
}



另外自定义Annotation还可以用在控制cache的粒度上,我可以给类或者方法加上自定义的Annotaion来确定是要给这个class 的所有方法cache,还是只是给某个方法cache.
具体的Annotation的source如下:
EntityCache 用来给class标注
@Target(ElementType.TYPE)   
@Retention(RetentionPolicy.RUNTIME)   
public @interface EntityCache {   
  int expireTime() default 0;
}


MethodCache用来给需要cache的方法标注
@Target(ElementType.METHOD)   
@Retention(RetentionPolicy.RUNTIME)   
public @interface MethodCache {   
	int expireTime() default 0;
}  

Example Class

@EntityCache
public interface MagazineService {
   public void updateMagazine(Magazine magazine);
   public void deleteMagazine(Magazine.MagazineId magazineId);
  
   public Magazine findMagazineByPrimaryKey(Magazine.MagazineId  magazineId);
   public void saveMagazine(Magazine magazine);
   public List<Magazine> getResultByNamedQuery(String name, Object... object);
  
   public List<Magazine> getAll(String beanName);
}


对这些Annotation的handle过程,请参考在上面的AnnotationMethodCacheInterceptor 这个拦截器.
0
0
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics