您的位置:首页 > 编程语言 > ASP

基于@AspectJ配置Spring AOP之一

2016-03-17 16:02 711 查看
from http://tech.it168.com/j/2007-08-30/200708302209432_1.shtml
一个简单的例子

在做好上节中所提到的前置工作后,我们就可以开始编写一个基于@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中


<bean class="org.springframework.aop.aspectj.annotation.


AnnotationAwareAspectJAutoProxyCreator"/>



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

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


<?xml version="1.0" encoding="UTF-8" ?>


<beans xmlns="http://www.springframework.org/schema/beans"


xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"


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


xsi:schemaLocation="http://www.springframework.org/schema/beans

 http://www.springframework.org/schema/beans/spring-beans-2.0.xsd 
 http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd"> 

<aop:aspectj-autoproxy /> ③基于@AspectJ切面的驱动器


<bean id="waiter" class="com.baobaotao.NaiveWaiter" />


<bean class="com.baobaotao.aspectj.example.PreGreetingAspect" />


</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动态代理。

@AspectJ语法基础

@AspectJ使用JDK 5.0注解和正规的AspectJ 5的切点表达式语言描述切面,由于Spring只支持方法的连接点,所以Spring仅支持部分AspectJ的切点语言。在这节时,我们将对AspectJ切点表达式语言进行必要的学习。

切点表达式函数

AspectJ 5的切点表达式由关键字和操作参数组成,如execution(* greetTo(..))的切点表达式,“execute”为关键字,而“* greetTo(..)”为操作参数。在这里,execute代表目标类执行某一方法,而“* greetTo(..)”是描述目标方法的匹配模式串,两者联合起来所表示的切点匹配目标类greetTo()方法的连接点。为了描述方便,我们将execution()称作函数,而将匹配串“*
greetTo(..)”称作函数的入参。

Spring支持9个@ApsectJ切点表达式函数,它们用不同的方式描述目标类的连接点,根据描述对象的不同,可以将它们大致分为4种类型:

 方法切点函数:通过描述目标类方法信息定义连接点;

 方法入参切点函数:通过描述目标类方法入参的信息定义连接点;

 目标类切点函数:通过描述目标类类型信息定义连接点;

 代理类切点函数:通过描述目标类的代理类的信息定义连接点;

这4种类型的切点函数,通过表 1进行说明:

表 1 切点函数

类别
函数
入参
说明
方法切点函数
execution()
方法
匹配模式串
表示满足某一匹配模式的所有目标类方法连接点。如execution(* greetTo(..))表示所有目标类中的greetTo()方法。
@annotation()
方法注
解类名
表示标注了特定注解的目标方法连接点。如@annotation(com.baobaotao.anno.NeedTest)表示任何标注了@NeedTest注解的目标类方法。
方法入参切点函数
args()
类名
通过判别目标类方法运行时入参对象的类型定义指定连接点。如args(com.baobaotao.Waiter)表示所有有且仅有一个按类型匹配于Waiter的入参的方法。
@args()
类型注
解类名
通过判别目标方法的运行时入参对象的类是否标注特定注解来指定连接点。如@args(com.baobaotao.Monitorable)表示任何这样的一个目标方法:它有一个入参且入参对象的类标注@Monitorable注解。
目标类切点函数
within()
类名匹配串
表示特定域下的所有连接点。如within(com.baobaotao.service.*)表示com.baobaotao.service包中的所有连接点,也即包中所有类的所有方法,而within(com.baobaotao.service.*Service)表示在com.baobaotao.service包中,所有以Service结尾的类的所有连接点。
target()
类名
假如目标类按类型匹配于指定类,则目标类的所有连接点匹配这个切点。如通过target(com.baobaotao.Waiter)定义的切点,Waiter、以及Waiter实现类NaiveWaiter中所有连接点都匹配该切点。
@within()
类型注解类名
假如目标类按类型匹配于某个类A,且类A标注了特定注解,则目标类的所有连接点匹配这个切点。
如@within(com.baobaotao.Monitorable)定义的切点,假如Waiter类标注了@Monitorable注解,则Waiter以及Waiter实现类NaiveWaiter类的所有连接点都匹配。
@target()
类型注解类名
目标类标注了特定注解,则目标类所有连接点匹配该切点。如@target(com.baobaotao.Monitorable),假如NaiveWaiter标注了@Monitorable,则NaiveWaiter所有连接点匹配切点。
代理类切点函数
this()
类名
代理类按类型匹配于指定类,则被代理的目标类所有连接点匹配切点。这个函数比较难理解,这里暂不举例,留待后面详解。
@AspectJ除上表中所列的函数外,还有call()、initialization()、 preinitialization()、 staticinitialization()、 get()、 set()、handler()、 adviceexecution()、 withincode()、
cflow()、 cflowbelow()、 if()、 @this()以及@withincode()等函数,这些函数在Spring中不能使用,否则会抛出IllegalArgumentException异常。在不特别声明的情况下,本书中所讲@AspectJ函数均指表 1中所列的函数。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: