`
elicer
  • 浏览: 133396 次
  • 性别: 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
分享到:
评论

相关推荐

    自定义Annotation例子

    自定义Annotation允许开发者创建自己的注解类型,以满足特定的需求或规范,例如记录代码元信息、实现代码生成、运行时检查等。下面我们将深入探讨自定义Annotation的例子及其相关知识点。 首先,了解如何定义一个...

    EJB+Annotation实现AOP的DEMO

    这篇博客"使用EJB+Annotation实现AOP的DEMO"主要介绍了如何在EJB中利用注解(Annotation)来实现AOP的功能。在Java EE中,EJB 3.0及后续版本引入了大量的注解,使得开发者可以免去编写XML配置文件,直接在代码中声明...

    基于annotation的aop实现

    基于Annotation的AOP实现是Spring框架的一个重要特性,它极大地简化了AOP的使用。在本篇文章中,我们将深入探讨基于Annotation的AOP实现,特别是动态代理的理念。 首先,了解什么是AOP。AOP的核心概念是“切面”...

    Spring_Annotation_AOP

    在本资料"Spring_Annotation_AOP"中,我们将深入探讨Spring框架如何利用注解实现AOP,以及其背后的原理和实践应用。 面向切面编程(AOP)是一种编程范式,旨在提高代码的可维护性和可重用性,通过将关注点分离,...

    Spring Boot中自定义注解结合AOP实现主备库切换问题

    本文将详细介绍如何使用自定义注解结合 AOP 实现主备库切换问题。 1. 自定义注解 自定义注解是 Spring Boot 中的一种非常重要的机制,用于标记方法或类,实现一些特殊的逻辑。在本例中,我们定义了一个名为 `...

    2个案例 自定义annotation进行查询对象封装 itext 打印案例

    在IT行业中,自定义注解(Annotation)是Java编程语言中的一个重要特性,它允许程序员在代码中嵌入元数据,增强了代码的可读性和可维护性。这些元数据可以被编译器或运行时环境用来执行特定的任务,例如代码分析、...

    spring aop 自定义缓存实现

    这个实例中的压缩包可能包含了配置文件、源代码和测试案例,你可以通过解压`demo-caching-with-spring-aop-master`来查看完整的实现,学习如何配置Spring AOP,以及如何定义和使用自定义缓存注解。 总的来说,...

    spring中自定义注解(annotation)与AOP中获取注解

    在Spring框架中,自定义注解(Annotation)和AOP(面向切面编程)的结合使用,极大地增强了代码的可读性和可维护性。本文将深入探讨如何在Spring中创建自定义注解以及如何在AOP中有效地获取并利用这些注解。 首先,...

    spring学习之五“AOP概念及使用Annotation的实现”

    在Spring框架中,有两种主要的方式来实现AOP:基于XML配置和基于注解。本篇“spring学习之五”主要介绍的是注解驱动的AOP实现。 1. **定义切面**:在Spring中,我们通常通过创建一个带有`@Aspect`注解的类来定义切...

    Java自定义注解Annotation的使用

    ### Java自定义注解Annotation的使用 #### 1. 前言 自从JDK 1.5引入了注解这一特性以来,它已经成为Java开发中的一个重要组成部分。注解最初是为了推动EJB 3.0的普及和发展而设计的,其目的是减少配置文件的使用,...

    自定义的Annotation

    本文将深入探讨如何实现自定义的Annotation。 首先,我们要了解Annotation的基本结构。在MapKit框架中,`MKAnnotation`是所有标注的基础协议,它规定了几个关键属性,如坐标(`coordinate`)、标题(`title`)和副...

    自定义Annotation注解

    本教程将通过一个简单的自定义Annotation注解示例——`CustomAnnotationDemo`,帮助新手了解如何创建和使用自定义注解。 首先,让我们看看如何定义一个自定义注解。在Java中,注解的定义使用`@interface`关键字。...

    SpringBoot-自定义注解AOP实现及拦截器示例

    接下来,我们将探讨如何使用AOP来处理自定义注解。Spring AOP支持基于注解的切面,这意味着我们可以定义一个切面类,其中包含一个或多个通知(advisors),这些通知会在匹配注解的方法执行前后被调用。例如: ```...

    SpringMVC利用AOP实现自定义注解记录日志

    本篇将深入探讨如何利用AOP和自定义注解来实现日志记录功能。 首先,我们需要了解AOP的基本概念。AOP的核心是切面(Aspect),它封装了应用程序中的某个关注点,如日志。切面可以理解为一系列相互关联的方法和通知...

    Spring Boot+Redis+拦截器+自定义Annotation如何实现接口自动幂等.docx

    Spring Boot+Redis+拦截器+自定义Annotation实现接口自动幂等 在实际的开发项目中,一个对外裸露的接口往往会濒临无数次哀求,我们来说明一下幂等的概念:随意多次执行所产生的影响均与一次执行的影响相同。根据这...

    学习Spring笔记_AOP_Annotation实现和XML实现

    这篇“学习Spring笔记_AOP_Annotation实现和XML实现”主要探讨了如何在Spring中利用注解和XML配置来实现AOP的概念。 AOP,全称Aspect-Oriented Programming,是一种编程范式,旨在将关注点分离,让开发者可以更专注...

    自定义annotationProcessor

    在本主题中,我们将深入探讨如何创建和使用自定义的`annotationProcessor`,以及它在IDEA中的应用。 首先,让我们了解什么是`annotationProcessor`。`annotationProcessor`,也称为注解处理器,是在Java编译过程中...

Global site tag (gtag.js) - Google Analytics