Spring2.5 注解 Aspect 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进行标注,将使其成为一个切面类: 
PreGreetingAspect 
Java代码   收藏代码
  1. package com.baobaotao.aspectj.aspectj;   
  2.   
  3. import org.aspectj.lang.annotation.Aspect;   
  4. import org.aspectj.lang.annotation.Before;   
  5.   
  6. @Aspect ①通过该注解将PreGreetingAspect标识为一个切面   
  7. public class PreGreetingAspect{   
  8.    @Before("execution(* greetTo(..))") ②定义切点和增强类型   
  9.    public void beforeGreeting(){③增强的横切逻辑   
  10.       System.out.println("How are you");   
  11.    }   
  12. }   


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

 
图 1 切面的信息构成 

PreGreetingAspect类通过注解和代码,将切点、增强类型和增强的横切逻辑揉合到一个类中,使切面的定义浑然天成。如果在低版本Spring AOP中,你必须同时创建增强类,切点类以及切面类,并使三者联合表达相同的信息。 

NaiveWaiter是一个Bean,它拥有一个greetTo()的方法,这个方法连接点匹配于上面我们通过@AspectJ所定义的切点,为了方便后续的说明,我们给出NaiveWaiter的代码: 

Java代码   收藏代码
  1. package com.baobaotao;  
  2.   
  3. public class NaiveWaiter implements Waiter {  
  4.    public void greetTo(String clientName) {  
  5.       System.out.println("NaiveWaiter:greet to "+clientName+"...");  
  6.    }  
  7.   
  8.    public void serveTo(String clientName){  
  9.       System.out.println("NaiveWaiter:serving "+clientName+"...");  
  10.    }  
  11. }  

虽然可以通过编程的方式织入切面,但一般情况下,我们还是使用Spring的配置自动完成创建代理织入切面的工作。 
Java代码   收藏代码
  1. <bean id="waiter" class="com.baobaotao.NaiveWaiter" /> ①目标Bean  
  2. ②使用了@AspectJ注解的切面类  
  3. <bean class="com.baobaotao.aspectj.example.PreGreetingAspect" />  
  4. ③自动代理创建器,自动将@AspectJ注解切面类织入到目标Bean中  
  5. <bean class="org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator"/>   

AnnotationAwareAspectJAutoProxyCreator能够将@AspectJ注解切面的自动织入到目标Bean中。这里,PreGreetingAspect是使用了@AspectJ注解描述的切面类,而NaiveWaiter是匹配切点的目标类。 

如果使用基于Schema的aop命名空间进行配置,事情就更简单了: 

Java代码   收藏代码
  1. <?xml version="1.0" encoding="UTF-8" ?>  
  2. <beans xmlns="http://www.springframework.org/schema/beans"  
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  4. xmlns:aop="http://www.springframework.org/schema/aop" ①  
  5. xsi:schemaLocation="http://www.springframework.org/schema/beans   
  6. http://www.springframework.org/schema/beans/spring-beans-2.0.xsd   
  7. http://www.springframework.org/schema/aop ②   
  8. http://www.springframework.org/schema/aop/spring-aop-2.0.xsd">   
  9.   
  10.    <aop:aspectj-autoproxy /> ③基于@AspectJ切面的驱动器  
  11.    <bean id="waiter" class="com.baobaotao.NaiveWaiter" />  
  12.    <bean class="com.baobaotao.aspectj.example.PreGreetingAspect" />  
  13. </beans>  


首先,在配置文件中引入aop命名空间,如①、②处所示,然后通过aop命名空间的<aop:aspectj-autoproxy />声明自动为Spring容器中那些匹配@AspectJ切面的Bean创建代理,织入切面。当然,Spring在内部依旧采用AnnotationAwareAspectJAutoProxyCreator进行自动代理的创建工作,但具体实现的细节已经被<aop:aspectj-autoproxy />隐藏起来了。 

<aop:aspectj-autoproxy />有一个proxy-target-class属性,默认为false,表示使用JDK动态代理织入增强,当配置为<aop:aspectj-autoproxy proxy-target-class="true" />时,表示使用CGLib动态代理技术织入增强。不过即使proxy-target-class设置为false,如果目标类没有声明接口,则Spring将自动使用CGLib动态代理。 

下面给出一些常见切入点表达式的例子。 

任意公共方法的执行: 
execution(public * *(..)) 

任何一个以“set”开始的方法的执行: 
execution(* set*(..)) 

AccountService 接口的任意方法的执行: 
execution(* com.xyz.service.AccountService.*(..)) 
这里 AccountService 是接口名 ; * 是方法名 

定义在service包里的任意方法的执行: 
execution(* com.xyz.service.*.*(..)) 
这里第一个*表示类, 第2个* 表示方法 

定义在service包或者子包里的任意方法的执行: 
execution(* com.xyz.service..*.*(..)) 
这里..表示包或者子包 第一个*表示类, 第2个* 表示方法 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值