日期:2014-05-17  浏览次数:20993 次

Spring2.5 注解 Aspect AOP (转)

在低版本Spring中定义一个切面是比较麻烦的,需要实现特定的接口,并进行一些较为复杂的配置,低版本Spring AOP的配置是被批评最多的地方。Spring听取这方面的批评声音,并下决心彻底改变这一现状。在Spring2.0中,Spring AOP已经焕然一新,你可以使用@AspectJ注解非常容易的定义一个切面,不需要实现任何的接口。
Spring2.0采用 @AspectJ注解对POJO进行标注,从而定义一个包含切点信息和增强横切逻辑的切面,Spring 2.0可以将这个切面织入到匹配的目标Bean中。@AspectJ注解使用AspectJ切点表达式语法进行切点定义,可以通过切点函数、运算符、通配 符等高级功能进行切点定义,拥有强大的连接点描述能力。在你学习基于@AspectJ的切面技术后,恐怕你就再也没有兴趣使用低版本Spring AOP的实现技术了,毕竟马落桃花马前雪,两者的易用性、便捷性是不可同日而语的。
着手使用@AspectJ
??? 我们知道在低版本的Spring AOP中,你必须使用Pointcut和Advice接口描述切点和增强,并用Advisor组合两者描述一个切面,@AspectJ则采用JDK 5.0的注解技术描述切点和增强类型,而增强的横切逻辑就在被标注的POJO中定义。
使用前的准备

??? 在使用@AspectJ之前,首先你得保证你所使用的JDK的版本是5.0及以上版本,否则无法使用注解技术。 Spring在处理@Aspect注解表达式时,需要使用位于<SPRING_HOME>/lib/asm/目录下asm的类包:asm- 2.2.2.jar、asm-commons-2.2.2.jar和asm-util-2.2.2.jar。asm是轻量级的字节码处理框架,因为 Java的反射机制无法获取入参名,Spring就利用asm处理@AspectJ中所描述的方法入参名。
此外,Spring采用AspectJ 提供的@AspectJ注解类库及相应的解析类库,它位于<SPRING_HOME>/lib/aspectj目录下,将目录下的 aspectjrt.jar和aspectjweaver.jar类包加入类路径中。

一个简单的例子
??? 在做好上节中所提到的前置工作后,我们就可以开始编写一个基于@AspectJ的切面了,首先来看一个简单的例子,以便对@AspectJ有一个切身的认 识。
下面,我们用@AspectJ注解对一个POJO进行标注,将使其成为一个切面类:
代码清单 1 PreGreetingAspect:切面

package com.baobaotao.aspectj.aspectj; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; @Aspect ①通过该注解将PreGreetingAspect标识为一个切面 public class PreGreetingAspect{ @Before("execution(* greetTo(..))") ②定义切点和增强类型 public void beforeGreeting(){③增强的横切逻辑 System.out.println("How are you"); } }

???? 我们“惊奇”地发现这个切面没有实现任何特殊的接口,它只是一个普通的POJO。它特殊的地方在于使用了@AspectJ注解。
首先,在 PreGreetingAspect类定义处,标注了一个@Aspectj注解,第三方处理程序就可以通过类是否拥有@Aspectj注解判断其是否是一 个切面,如①所示。
其 次,在beforeGreeting()方法标签处,标注了@Before注解,并为该注解提供了成员值"execution(* greetTo(..))",如②所示。②处的注解提供了两个信息:@Before注解表示该增强是前置增强,而成员值通过@ApsectJ切点表达式语 法定义切点:即在目标类的greetTo()方法上织入增强,greetTo()方法可以带任意的入参和任意的返回值。
最后,在③处的 beforeGreeting()方法是增强的横切逻辑,该横切逻辑在目标方法前调用,我们通过下图描述这种关系:

图 1 切面的信息构成
??? PreGreetingAspect类通过注解和代码,将切点、增强类型和增强的横切逻辑揉合到一个类中,使切面的定义浑然天成。如果在低版本 Spring AOP中,你必须同时创建增强类,切点类以及切面类,并使三者联合表达相同的信息。
NaiveWaiter是一个Bean,它拥 有一个greetTo()的方法,这个方法连接点匹配于上面我们通过@AspectJ所定义的切点,为了方便后续的说明,我们给出NaiveWaiter 的代码:

package com.baobaotao;public class NaiveWaiter implements Waiter {public void greetTo(String clientName) {System.out.println("NaiveWaiter:greet to "+clientName+"...");}public void serveTo(String clientName){System.out.println("NaiveWaiter:serving "+clientName+"...");}}

下 面,我们通过org.springframework.aop.aspectj.annotation.AspectJProxyFactory为 NaiveWaiter生成织入PreGreetingAspect切面的代理,如代码清单 2所示:
代码清单 2 AspectJProxyTest

package com.baobaotao.aspectj.example;import org.springframework.aop.aspectj.annotation.AspectJProxyFactory;import com.baobaotao.NaiveWaiter;import com.baobaotao.Waiter;public class AspectJProxyTest {Waiter target = new NaiveWaiter();AspectJProxyFactory factory = new AspectJProxyFactory();factory.setTarget(target); ① 设置目标对象factory.addAspect(PreGreetingAspect.class); ②添加切面类Waiter proxy = factory.getProxy(); ③ 生成织入切面的代理对象proxy.greetTo("John");proxy.serveTo("John");}}

Spring 使用AspectJProxyFactory织入基于@AspectJ切面的工作。在①处,设置了目标对象,在②处添加一个切面类,该类必须是带 @AspectJ注解的类,在③处,我们就可以获取织入切面的代理对象了。
接下来,我们直接通过代理对象调用greetTo()和 serveTo()代码,它们产生以下的输出信息:
How are you ①表示greetTo()方法被成功地织入了切面
greet to John...
serving John...
通过①处的输出信息我们可以知道代理对象的greetTo()方法已经织入了 切面类所定义的增强逻辑了。

通过配置织入@AspectJ切面
虽然可以通过编程的方式织入切 面,但一般情况下,我们还是使用Spring的配置自动完成创建代理织入切面的工作。

<bean id="waiter" class="com.baobaotao.NaiveWaiter" /> ①目标Bean②使用了@AspectJ注解的切面类<bean class="com.baobaotao.aspectj.example.PreGreetingAspect" />③自动代理创建器,自动将@AspectJ注解切面类织入到目标Bean