利用Spring AOP处理自定义注解

(http://hi.baidu.com/coolcooldool/item/1ee37d258542b48f9d63d10a)

Spring3.0中加入了对缓存的注解支持,即当你使用ehcache时可以使用例如@cachable等注解,这十分方便,省去了80%的缓存代码量(我自己感觉,因为自己操作缓存挺麻烦的)。

        但是却遇到了一个问题,我的项目到时候需要tomcat集群部署,前端用F5做负载均衡,这样也就涉及到了缓存同步问题,虽然ehcache也有standalone server 、terracotta等技术来实现同步,但是至少我没能成功完成,总是有各种问题。

        而且还有一个问题就是当集群部署的时候Java 中 synchronized 关键字失效,这就要求必须自己控制“锁”,以及锁的争用。于是我想到了用memcached比较适合,它的cas()也就是比较更新方法正好可以用于对锁获取时的控制。其实查看了一下ehcache的文档,它也有明确加锁模式,但是由于不参实现ehcache的单独部署,所以就采用memcached做缓存实现,client采用Xmemcached开发。

       Xmemcached是不支持注解的,所以决定自己实现一下@cachable类似注解,其实核心也就是利用spring 的 aop技术对注解方法进行拦截加以处理。以下为简单示例。

      在Maven中加入需要的包

这是我的maven文件片段,有一点要说的就是aspectj包,在maven仓库中搜索只能找到1.5.4版本,这个版本是不支持JDK5以上的版本的,如果使用JDK6的版本需要1.6.6以上的包,这个包的maven依赖关系可以在http://mvnrepository.com/这个网站找到。

        在web.xml中加入spring支持,这个就不说了,在spring配置文件中打开aop功能

       

之前要加上头文件声明 :

xmlns:aop="http://www.springframework.org/schema/aop"

http://www.springframework.org/schema/aop

 http://www.springframework.org/schema/aop/spring-aop-3.0.xsd

分别放到相应的位置。

       建立自定义接口Young.java

       

建议一个使用这个注解的类YoungService.java



好多好多的中文或者英文资料讲述自定义注解的时候也就到此为止了,然后写一个测试所谓的处理器,把注解的类当成参数传到处理器中,然后使用java的反射机制判断某个方法是不是有注解,有的话怎样处理,这样手动调用处理器处理自定义注解,我如果要手动调用,我还要注解干嘛啊!我有手动调用那时间,我写一个静态方法实现同样的功能好不好啊!不知道这些资料到底是怎么样了!

 

下面说一下我写处理器的思路,只是个人思路,好不好不能保证。

我使用spring 的aop机制去拦截加了注解的方法,获取到传入方法的参数,判断方法是不是要去运行,如果运行再得到方法返回参数,加以处理再返回给调用该方法的类。

用到cache上也就是,我拦截得到了传入的key,判断我的memcached里面是不是已经有了值,如果有了值就不真正的执行方法了,将取出来的值返回给调用该方法的类就可以了,如果没有值那么就执行方法取得值,将取得的值存一份到memcached中,然后返回给调用该方法的类。这样就与spring自带的@cachable注解的功能一样了。那么就开始写这个处理器,其实灰常简单:

说一下这个类,从最上面说起,@Aspect是aspectj包中的注解,该注解并不会使spring自动扫描并且实例化该类,所以还必须加上@Complnent注解,当然spring配置文件中得打开自动扫描:然后往下,@Around注解表明在切点方法的前后都要执行该处理器方法,那么切点在哪呢?切点就定义在了@Around后面的括号内也就是这个表达式:"execution (* org.y0ung.example.custom_annotation_with_spring.service.*.*(..)) && @annotation(young) && args(key)"这个表达式的意思是说指向这些方法,在org.y0ung.example.custom_annotation_with_spring.service包中的,所有类的(第一个*),所有方法(第二个*),并且该方法有注解@annotation(young)括号中的young是表示这个会做为参数传入处理器方法,也就是下面方法的第二个参数。而且这个方法还有一个参数args(key),并且这个key也做为参数传入到处理器方法。而ProceedingJoinPoint pjp参数是固有参数,你可以不加,但是要加一定要放到第一个位置。args(key)也可以不添加在@Around内,可以通过pjp.getArgs()方法获取传入参数。如果方法本来就有返回值,那么就要把上面的方法加上返回值Object,在方法中返回就可以了。

 

 

上面说了这么多好绕啊,要多看几遍才能看得懂。这些定义好了之后这个处理器基本就写完了,aroundYoung()方法内部就是利用那些传进来的参数做一些操作,如果需要执行目标方法则调用pjp.proceed()方法来调用。

 

以上只是一个简单示例,研究了两天才写出来的。

其实spring官方文档中还有一种注解的写法:

我我不知道为什么我像这样写始终都不能成功,也就是我那个方法上面的两行注释,所以只能按官方提供的第二种写法写

 

如果遇到的报错类似为:

error at ::0 can't find referenced pointcut anyPublicMethod

这种 错误,就是spring不能识别pointcut,可能表达式写错了。

网上一些资料说是aspectj版本太低了,我感觉应该不是,上面提到了,版本低了会报一个类似这样的错误:

error the @annotation pointcut expression is only supported at Java 5 compliance level or above

 

程序源代码请下载以下图片,下载后将后缀名改为rar解压即可直接导入eclipse

 

参考的文章中只有一往篇写得正确:http://zywang.iteye.com/blog/974226

 

再补充一下,关于Point Cut还有一种更灵活的写法

@Pointcut("execution (* org.y0ung.example.custom_annotation_with_spring.service.*.*(..))")

public void youngPointCut1(){}

 

我可以定义一个这样的注解,然后按这样调用

@Around("youngPointCut1() && @annotation(young) && args(key)")

 public void aroundYoung(ProceedingJoinPoint pjp,Young young,String key) throws Throwable {

......

}

这样就可以实现灵活的控制不同参数,不同注解的各种方法。

 

当然也可以这样声明

@Pointcut("execution (* org.y0ung.example.custom_annotation_with_spring.service.*.*(..)) && @annotation(young) && args(key)")

public void youngPointCut(Young young,String key){}

这样调用 

@Around("youngPointCut()")

 public void aroundYoung(ProceedingJoinPoint pjp,Young young,String key) throws Throwable {

......

}

能看出这两种方法之间的区别吗?



你可能感兴趣的:(Java)