Spring进行面向切面编程(AOP



Spring进行面向切面编程(AOP

6.2.4.1. 前置通知(Before advice)
一个切面里使用 @Before 注解声明前置通知:

如果使用一个in-place 的切入点表达式,我们可以把上面的例子换个写法:

import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; @Aspect public class BeforeExample { @Before(“execution(* com.xyz.myapp.dao.*.*(..))”) public void doAccessCheck() { // … } }
6.2.4.2. 返回后通知(After returning advice)
返回后通知通常在一个匹配的方法返回的时候执行。使用 @AfterReturning 注解来声明:

import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.AfterReturning; @Aspect public class AfterReturningExample { @AfterReturning(“com.xyz.myapp.SystemArchitecture.dataAccessOperation()”) public void doAccessCheck() { // … } }
说明:你可以在同一个切面里定义多个通知,或者其他成员。我们只是在展示如何定义一个简单的通知。这些例子主要的侧重点是正在讨论的问题。
有时候你需要在通知体内得到返回的值。你可以使用以 @AfterReturning 接口的形式来绑定返回值:

import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.AfterReturning; @Aspect public class AfterReturningExample { @AfterReturning( pointcut=”com.xyz.myapp.SystemArchitecture.dataAccessOperation()”, returning=”retVal”) public void doAccessCheck(Object retVal) { // … } }
在 returning 属性中使用的名字必须对应于通知方法内的一个参数名。 当一个方法执行返回后,返回值作为相应的参数值传入通知方法。 一个 returning 子句也限制了只能匹配到返回指定类型值的方法。 (在本例子中,返回值是 Object 类,也就是说返回任意类型都会匹配)

6.2.4.3. 抛出后通知(After throwing advice)
抛出后通知在一个方法抛出异常后执行。使用 @AfterThrowing 注解来声明:

import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.AfterThrowing; @Aspect public class AfterThrowingExample { @AfterThrowing(“com.xyz.myapp.SystemArchitecture.dataAccessOperation()”) public void doRecoveryActions() { // … } }
你通常会想要限制通知只在某种特殊的异常被抛出的时候匹配,你还希望可以在通知体内得到被抛出的异常。 使用 throwing 属性不光可以限制匹配的异常类型(如果你不想限制,请使用 Throwable 作为异常类型),还可以将抛出的异常绑定到通知的一个参数上。

import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.AfterThrowing; @Aspect public class AfterThrowingExample { @AfterThrowing( pointcut=”com.xyz.myapp.SystemArchitecture.dataAccessOperation()”, throwing=”ex”) public void doRecoveryActions(DataAccessException ex) { // … } }
在 throwing 属性中使用的名字必须与通知方法内的一个参数对应。 当一个方法因抛出一个异常而中止后,这个异常将会作为那个对应的参数送至通知方法。 throwing 子句也限制了只能匹配到抛出指定异常类型的方法(上面的示例为 DataAccessException)。

6.2.4.4. 后通知(After (finally) advice)
不论一个方法是如何结束的,在它结束后(finally)后通知(After (finally) advice)都会运行。 使用 @After 注解来声明。这个通知必须做好处理正常返回和异常返回两种情况。通常用来释放资源。

import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.After; @Aspect public class AfterFinallyExample { @After(“com.xyz.myapp.SystemArchitecture.dataAccessOperation()”) public void doReleaseLock() { // … } }
6.2.4.5. 环绕通知(Around Advice)
最后一种通知是环绕通知。环绕通知在一个方法执行之前和之后执行。 它使得通知有机会既在一个方法执行之前又在执行之后运行。并且,它可以决定这个方法在什么时候执行,如何执行,甚至是否执行。 环绕通知经常在在某线程安全的环境下,你需要在一个方法执行之前和之后共享某种状态的时候使用。 请尽量使用最简单的满足你需求的通知。(比如如果前置通知(before advice)也可以适用的情况下不要使用环绕通知)。

环绕通知使用 @Around 注解来声明。通知的第一个参数必须是 ProceedingJoinPoint 类型。 在通知体内,调用ProceedingJoinPoint 的 proceed() 方法将会导致潜在的连接点方法执行。 proceed 方法也可能会被调用并且传入一个 Object[]对象-该数组将作为方法执行时候的参数。

当传入一个 Object[] 对象的时候,处理的方法与通过AspectJ编译器处理环绕通知略有不同。 对于使用传统AspectJ语言写的环绕通知来说,传入参数的数量必须和传递给环绕通知的参数数量匹配(不是后台的连接点接受的参数数量),并且特定顺序的传入参数代替了将要绑定给连接点的原始值(如果你看不懂不用担心)。 Spring采用的方法更加简单并且更好得和他的基于代理(proxy-based),只匹配执行的语法相适用。 如果你适用AspectJ的编译器和编织器来编译为Spring而写的@AspectJ切面和处理参数,你只需要了解这一区别即可。 有一种方法可以让你写出100%兼容Spring AOP和AspectJ的,我们将会在后续的通知参数(advice parameters)的章节中讨论它。
import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.ProceedingJoinPoint; @Aspect public class AroundExample { @Around(“com.xyz.myapp.SystemArchitecture.businessService()”) public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable { // start stopwatch Object retVal = pjp.proceed(); // stop stopwatch return retVal; } }
方法的调用者得到的返回值就是环绕通知返回的值。 例如:一个简单的缓存切面,如果缓存中有值,就返回该值,否则调用proceed()方法。 请注意proceed可能在通知体内部被调用一次,许多次,或者根本不被调用。

6.2.4.6. 通知参数(Advice parameters)
Spring 2.0 提供了完整的通知类型 – 这意味着你可以在通知签名中声明所需的参数,(就像在以前的例子中我们看到的返回值和抛出异常一样)而不总是使用Object[]。 我们将会看到如何在通知体内访问参数和其他上下文相关的值。首先让我们看以下如何编写普通的通知以找出正在被通知的方法。

6.2.4.6.1. 访问当前的连接点
任何通知方法可以将第一个参数定义为 org.aspectj.lang.JoinPoint 类型 (环绕通知需要定义为 ProceedingJoinPoint 类型的, 它是 JoinPoint 的一个子类。) JoinPoint 接口提供了一系列有用的方法, 比如 getArgs()(返回方法参数)、getThis()(返回代理对象)、getTarget()(返回目标)、getSignature()(返回正在被通知的方法相关信息)和 toString()(打印出正在被通知的方法的有用信息)。

6.2.4.6.2. 传递参数给通知(Advice)
我们已经看到了如何绑定返回值或者异常(使用后置通知(after returning)和异常后通知(after throwing advice)。 为了可以在通知(adivce)体内访问参数,你可以使用 args 来绑定。 如果在一个参数表达式中应该使用类型名字的地方使用一个参数名字,那么当通知执行的时候对应的参数值将会被传递进来。 可能给出一个例子会更好理解。假使你想要通知(advise)接受某个Account对象作为第一个参数的DAO操作的执行,你想要在通知体内也能访问到account对象,你可以写如下的代码:

@Before(“com.xyz.myapp.SystemArchitecture.dataAccessOperation() &&” + “args(account,..)”) public void validateAccount(Account account) { // … }
切入点表达式的 args(account,..) 部分有两个目的: 首先它保证了只会匹配那些接受至少一个参数的方法的执行,而且传入的参数必须是 Account 类型的实例, 其次它使得可以在通知体内通过 account 参数来访问那个account参数。

另外一个办法是定义一个切入点,这个切入点在匹配某个连接点的时候“提供”了一个Account对象, 然后直接从通知中访问那个命名的切入点。你可以这样写:

@Pointcut(“com.xyz.myapp.SystemArchitecture.dataAccessOperation() &&” + “args(account,..)”) private void accountDataAccessOperation(Account account) {} @Before(“accountDataAccessOperation(account)”) public void validateAccount(Account account) { // .. }
如果想要知道更详细的内容,请参阅 AspectJ 编程指南。

代理对象(this)、目标对象(target) 和注解(@within, @target, @annotation, @args)都可以用一种简单格式绑定。 以下的例子展示了如何使用 @Auditable 注解来匹配方法执行,并提取AuditCode。

首先是 @Auditable 注解的定义:

@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface Auditable { AuditCode value(); }
然后是匹配 @Auditable 方法执行的通知:

@Before(“com.xyz.lib.Pointcuts.anyPublicMethod() && ” + “@annotation(auditable)”) public void audit(Auditable auditable) { AuditCode code = auditable.value(); // … }
6.2.4.6.3. 决定参数名
绑定在通知上的参数依赖切入点表达式的匹配名,并借此在(通知(advice)和切入点(pointcut))的方法签名中声明参数名。 参数名 无法 通过Java反射来获取,所以Spring AOP使用如下的策略来决定参数名字:

如果参数名字已经被用户明确指定,则使用指定的参数名: 通知(advice)和切入点(pointcut)注解有一个额外的”argNames”属性,该属性用来指定所注解的方法的参数名 – 这些参数名在运行时是 可以 访问的。例子如下:

@Before( value=”com.xyz.lib.Pointcuts.anyPublicMethod() && ” + “@annotation(auditable)”, argNames=”auditable”) public void audit(Auditable auditable) { AuditCode code = auditable.value(); // … }
如果一个@AspectJ切面已经被AspectJ编译器(ajc)编译过了,那么就不需要再添加 argNames 参数了,因为编译器会自动完成这一工作。
使用 ‘argNames’ 属性有点不那么优雅,所以如果没有指定’argNames’ 属性, Spring AOP 会寻找类的debug信息,并且尝试从本地变量表(local variable table)中来决定参数名字。 只要编译的时候使用了debug信息(至少要使用 ‘-g:vars’ ),就可获得这些信息。 使用这个flag编译的结果是: (1)你的代码将能够更加容易的读懂(反向工程), (2)生成的class文件会稍许大一些(不重要的), (3)移除不被使用的本地变量的优化功能将会失效。 换句话说,你在使用这个flag的时候不会遇到任何困难。

如果不加上debug信息来编译的话,Spring AOP将会尝试推断参数的绑定。 (例如,要是只有一个变量被绑定到切入点表达式(pointcut expression)、通知方法(advice method)将会接受这个参数, 这是显而易见的)。 如果变量的绑定不明确,将会抛出一个 AmbiguousBindingException 异常。

如果以上所有策略都失败了,将会抛出一个 IllegalArgumentException 异常。

6.2.4.6.4. 处理参数
我们之前提过我们将会讨论如何编写一个 带参数的 的proceed()调用,使得不论在Spring AOP中还是在AspectJ都能正常工作。 解决方法是保证通知签名依次绑定方法参数。比如说:

@Around(“execution(List find*(..)) &&” + “com.xyz.myapp.SystemArchitecture.inDataAccessLayer() && ” + “args(accountHolderNamePattern)”) public Object preProcessQueryPattern(ProceedingJoinPoint pjp, String accountHolderNamePattern) throws Throwable { String newPattern = preProcess(accountHolderNamePattern); return pjp.proceed(new Object[] {newPattern}); }
大多数情况下你都会这样绑定(就像上面的例子那样)。

6.2.4.7. 通知(Advice)顺序
如果有多个通知想要在同一连接点运行会发生什么?Spring AOP 的执行通知的顺序跟AspectJ的一样。 在“进入”连接点的情况下,最高优先级的通知会先执行(所以上面给出的两个前置通知(before advice)中,优先级高的那个会先执行)。 在“退出”连接点的情况下,最高优先级的通知会最后执行。(所以上面给出的两个前置通知(before advice)中,优先级高的那个会第二个执行)。 对于定义在相同切面的通知,根据声明的顺序来确定执行顺序。比如下面这个切面:

@Aspect public class AspectWithMultipleAdviceDeclarations { @Pointcut(“execution(* foo(..))”) public void fooExecution() {} @Before(“fooExecution()”) public void doBeforeOne() { // .. } @Before(“fooExecution()”) public void doBeforeTwo() { // .. } @AfterReturning(“fooExecution()”) public void doAfterOne() { // .. } @AfterReturning(“fooExecution()”) public void doAfterTwo() { //.. } }
这样,假使对于任何一个名字为foo的方法的执行, doBeforeOne、doBeforeTwo、doAfterOne 和 doAfterTwo 通知方法都需要运行。 执行顺序将按照声明的顺序来确定。在这个例子中,执行的结果会是:

doBeforeOne doBeforeTwo foo doAfterOne doAfterTwo
换言之,因为doBeforeOne先定义,它会先于doBeforeTwo执行,而doAfterTwo后于doAfterOne定义,所以它会在doAfterOne之后执行。 只需要记住通知是按照定义的顺序来执行的就可以了。 – 如果想要知道更加详细的内容,请参阅AspectJ编程指南。

当定义在 不同的 切面里的两个通知都需要在一个相同的连接点中运行,那么除非你指定,否则执行的顺序是未知的。 你可以通过指定优先级来控制执行顺序。在Spring中可以在切面类中实现 org.springframework.core.Ordered 接口做到这一点。 在两个切面中,Ordered.getValue() 方法返回值较低的那个有更高的优先级。

6.2.5. 引入(Introductions)
引入(Introductions)(在AspectJ中被称为inter-type声明)使得一个切面可以定义被通知对象实现一个给定的接口,并且可以代表那些对象提供具体实现。

使用 @DeclareParents注解来定义引入。这个注解被用来定义匹配的类型拥有一个新的父亲。 比如,给定一个接口 UsageTracked,然后接口的具体实现 DefaultUsageTracked 类, 接下来的切面声明了所有的service接口的实现都实现了 UsageTracked 接口。(比如为了通过JMX输出统计信息)。

@Aspect public class UsageTracking { @DeclareParents(value=”com.xzy.myapp.service.*+”, defaultImpl=DefaultUsageTracked.class) public static UsageTracked mixin; @Before(“com.xyz.myapp.SystemArchitecture.businessService() &&” + “this(usageTracked)”) public void recordUsage(UsageTracked usageTracked) { usageTracked.incrementUseCount(); } }
实现的接口通过被注解的字段类型来决定。@DeclareParents 注解的 value 属性是一个AspectJ的类型模式:- 任何匹配类型的bean都会实现 UsageTracked 接口。 请注意,在上面的前置通知(before advice)的例子中,service beans 可以直接用作UsageTracked 接口的实现。 如果需要编程式的来访问一个bean,你可以这样写:

UsageTracked usageTracked = (UsageTracked) context.getBean(“myService”);
6.2.6. 切面实例化模型
这是一个高级主题…
默认情况下,在application context中每一个切面都会有一个实例。 AspectJ 把这个叫做单个实例化模型(singleton instantiation model)。 也可以用其他的生命周期来定义切面:- Spring支持AspectJ的 perthis 和 pertarget 实例化模型 (现在还不支持percflow、percflowbelow 和 pertypewithin )。

一个”perthis” 切面的定义:在 @Aspect 注解中指定perthis 子句。 让我们先来看一个例子,然后解释它是如何运作的:

@Aspect(“perthis(com.xyz.myapp.SystemArchitecture.businessService())”) public class MyAspect { private int someState; @Before(com.xyz.myapp.SystemArchitecture.businessService()) public void recordServiceUsage() { // … } }
这个perthis子句的效果是每个独立的service对象执行时都会创建一个切面实例(切入点表达式所匹配的连接点上的每一个独立的对象都会绑定到’this’上)。 service对象的每个方法在第一次执行的时候创建切面实例。切面在service对象失效的同时失效。 在切面实例被创建前,所有的通知都不会被执行,一旦切面对象创建完成,定义的通知将会在匹配的连接点上执行,但是只有当service对象是和切面关联的才可以。 如果想要知道更多关于per-clauses的信息,请参阅 AspectJ 编程指南。

‘pertarget’实例模型的跟“perthis”完全一样,只不过是为每个匹配于连接点的独立目标对象创建一个切面实例。

6.2.7. 例子
现在你已经看到了每个独立的部分是如何运作的了,是时候把他们放到一起做一些有用的事情了!

因为乐观锁的关系,有时候business services可能会失败(有人甚至在一开始运行事务的时候就失败了)。如果重新尝试一下,很有可能就会成功。 对于business services来说,重试几次是很正常的(Idempotent操作不需要用户参与,否则会得出矛盾的结论) 我们可能需要透明的重试操作以避免让客户看见 OptimisticLockingFailureException 例外被抛出。 很明显,在一个横切多层的情况下,这是非常有必要的,因此通过切面来实现是很理想的。

因为我们想要重试操作,我们会需要使用到环绕通知,这样我们就可以多次调用proceed()方法。下面是简单的切面实现:

@Aspect
public class OptimisticOperationExecutor implements Ordered {
private static final int DEFAULT_MAX_RETRIES = 2;
private int maxRetries = DEFAULT_MAX_RETRIES;
private int order = 1;
public void setMaxRetries(int maxRetries) {
this.maxRetries = maxRetries;
}
public int getOrder() {
return this.order;
}
public void setOrder(int order) {
this.order = order;
}
@Around(“com.xyz.myapp.SystemArchitecture.businessService()”)
public Object doOptimisticOperation(ProceedingJoinPoint pjp) throws Throwable {
int numAttempts = 0;
OptimisticLockingFailureException lockFailureException;
do {
numAttempts++;
try {
return pjp.proceed();
}
catch(OptimisticLockingFailureException ex) {
lockFailureException = ex;
}
}
while(numAttempts <= this.maxRetries);
throw lockFailureException;
}
}

 

请注意切面实现了 Ordered 接口,这样我们就可以把切面的优先级设定为高于事务通知(我们每次重试的时候都想要在一个全新的事务中进行)。 maxRetries 和 order 属性都可以在Spring中配置。 主要的动作在 doOptimisticOperation 这个环绕通知中发生。 请注意这个时候我们所有的 businessService() 方法都会使用这个重试策略。 我们首先会尝试处理,然后如果我们得到一个OptimisticLockingFailureException 意外,我们只需要简单的重试,直到我们耗尽所有预设的重试次数。


对应的Spring配置如下:

<</span>aop:aspectj-autoproxy/>
<</span>bean id=”optimisticOperationExecutor”
class=”com.xyz.myapp.service.impl.OptimisticOperationExecutor”>
<</span>property name=”maxRetries” value=”3″/>
<</span>property name=”order” value=”100″/>
</</span>bean>
为了改进切面,使之仅仅重试idempotent操作,我们可以定义一个 Idempotent 注解:

@Retention(RetentionPolicy.RUNTIME)
public @interface Idempotent {
// marker annotation
}
并且对service操作的实现进行注解。 这样如果你只希望改变切面使得idempotent的操作会尝试多次,你只需要改写切入点表达式,这样只有 @Idempotent 操作会匹配:

@Around(“com.xyz.myapp.SystemArchitecture.businessService() && ” + “@annotation(com.xyz.myapp.service.Idempotent)”) public Object doOptimisticOperation(ProceedingJoinPoint pjp) throws Throwable { … }
6.3. Schema-based AOP support
如果你无法使用Java 5,或者你比较喜欢使用XML格式,Spring2.0也提供了使用新的”aop”命名空间来定义一个切面。 和使用@AspectJ风格完全一样,切入点表达式和通知类型同样得到了支持,因此在这一节中我们将着重介绍新的 语法 和回顾前面我们所讨论的如何写一个切入点表达式和通知参数的绑定等等(Section 6.2, “@AspectJ支持”)。

使用本章所介绍的aop命名空间标签(aop namespace tag),你需要引入Appendix A, XML Schema-based configuration中提及的spring-aop schema。 参见Section A.2.6, “The aop schema”。

在Spring的配置文件中,所有的切面和通知器都必须定义在 元素内部。 一个application context可以包含多个。 一个 可以包含pointcut,advisor和aspect元素,并且三者必须按照这样的顺序进行声明。

6.3.1. 声明一个切面
有了schema的支持,切面就和常规的Java对象一样被定义成application context中的一个bean。 对象的字段和方法提供了状态和行为信息,XML文件则提供了切入点和通知信息。

切面使用来声明,backing bean(支持bean)通过 ref 属性来引用:

… …
切面的支持bean(上例中的”aBean”)可以象其他Spring bean一样被容器管理配置以及依赖注入。

6.3.2. 声明一个切入点
切入点可以在切面里面声明,这种情况下切入点只在切面内部可见。切入点也可以直接在下定义,这样就可以使多个切面和通知器共享该切入点。

一个描述service层中表示所有service执行的切入点可以如下定义:

注意切入点表达式本身使用了 Section 6.2, “@AspectJ支持” 中描述的AspectJ 切入点表达式语言。 如果你在Java 5环境下使用基于schema的声明风格,可参考切入点表达式类型中定义的命名式切入点,不过这在JDK1.4及以下版本中是不被支持的(因为依赖于Java 5中的AspectJ反射API)。 所以在JDK 1.5中,上面的切入点的另外一种定义形式如下:

假定你有 Section 6.2.3.3, “共享常见的切入点(pointcut)定义”中说描述的 SystemArchitecture 切面。

在切面里面声明一个切入点和声明一个顶级的切入点非常类似:


当需要连接子表达式的时候,’&&’在XML中用起来非常不方便,所以关键字’and’, ‘or’ 和 ‘not’可以分别用来代替’&&’, ‘||’ 和 ‘!’。

注意这种方式定义的切入点通过XML id来查找,并且不能定义切入点参数。在基于schema的定义风格中命名切入点支持较之@AspectJ风格受到了很多的限制。

6.3.3. 声明通知
和@AspectJ风格一样,基于schema的风格也支持5种通知类型并且两者具有同样的语义。

6.3.3.1. 通知(Advice)
Before通知在匹配方法执行前进入。在里面使用元素进行声明。


这里 dataAccessOperation 是一个顶级()切入点的id。 要定义内置切入点,可将 pointcut-ref 属性替换为pointcut 属性:


我们已经在@AspectJ风格章节中讨论过了,使用命名切入点能够明显的提高代码的可读性。

Method属性标识了提供了通知的主体的方法(doAccessCheck)。这个方法必须定义在包含通知的切面元素所引用的bean中。 在一个数据访问操作执行之前(执行连接点和切入点表达式匹配),切面中的”doAccessCheck”会被调用。

6.3.3.2. 返回后通知(After returning advice)
After returning通知在匹配的方法完全执行后运行。和Before通知一样,可以在里面声明。例如:


和@AspectJ风格一样,通知主体可以接收返回值。使用returning属性来指定接收返回值的参数名:


doAccessCheck方法必须声明一个名字叫 retVal 的参数。 参数的类型强制匹配,和先前我们在@AfterReturning中讲到的一样。例如,方法签名可以这样声明:

public void doAccessCheck(Object retVal) {…
6.3.3.3. 抛出异常后通知(After throwing advice)
After throwing通知在匹配方法抛出异常退出时执行。在 中使用after-throwing元素来声明:


和@AspectJ风格一样,可以从通知体中获取抛出的异常。 使用throwing属性来指定异常的名称,用这个名称来获取异常:


doRecoveryActions方法必须声明一个名字为 dataAccessEx 的参数。 参数的类型强制匹配,和先前我们在@AfterThrowing中讲到的一样。例如:方法签名可以如下这般声明:

public void doRecoveryActions(DataAccessException dataAccessEx) {…
6.3.3.4. 后通知(After (finally) advice)
After (finally)通知在匹配方法退出后执行。使用 after 元素来声明:


6.3.3.5. 通知
Around通知是最后一种通知类型。Around通知在匹配方法运行期的“周围”执行。 它有机会在目标方法的前面和后面执行,并决定什么时候运行,怎么运行,甚至是否运行。 Around通知经常在需要在一个方法执行前或后共享状态信息,并且是线程安全的情况下使用(启动和停止一个计时器就是一个例子)。 注意选择能满足你需求的最简单的通知类型(i.e.如果简单的before通知就能做的事情绝对不要使用around通知)。

Around通知使用 aop:around 元素来声明。 通知方法的第一个参数的类型必须是 ProceedingJoinPoint 类型。 在通知的主体中,调用 ProceedingJoinPoint的proceed() 方法来执行真正的方法。 proceed 方法也可能会被调用并且传入一个 Object[] 对象 – 该数组将作为方法执行时候的参数。 参见 Section 6.2.4.5, “环绕通知(Around Advice)” 中提到的一些注意点。


doBasicProfiling 通知的实现和@AspectJ中的例子完全一样(当然要去掉注解):

public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable { // start stopwatch Object retVal = pjp.proceed(); // stop stopwatch return retVal; }
6.3.3.6. 通知参数
Schema-based声明风格和@AspectJ支持一样,支持通知的全名形式 – 通过通知方法参数名字来匹配切入点参数。 参见Section 6.2.4.6, “通知参数(Advice parameters)” 获取详细信息。

如果你希望显式指定通知方法的参数名(而不是依靠先前提及的侦测策略),可以通过 arg-names 属性来实现。示例如下:

The arg-names attribute accepts a comma-delimited list of parameter names.

arg-names属性接受由逗号分割的参数名列表。

6.3.3.7. 通知顺序
当同一个切入点(执行方法)上有多个通知需要执行时,执行顺序规则在 Section 6.2.4.7, “通知(Advice)顺序” 已经提及了。 切面的优先级通过切面的支持bean是否实现了Ordered接口来决定。

6.3.4. 引入
Intrduction (在AspectJ中成为inter-type声明)允许一个切面声明一个通知对象实现指定接口,并且提供了一个接口实现类来代表这些对象。

在 aop:aspect 内部使用 aop:declare-parents 元素定义Introduction。 该元素用于用来声明所匹配的类型有了一个新的父类型(所以有了这个名字)。 例如,给定接口 UsageTracked,以及这个接口的一个实现类 DefaultUsageTracked, 下面声明的切面所有实现service接口的类同时实现 UsageTracked 接口。(比如为了通过JMX暴露statistics。)

The class backing the usageTracking bean would contain the method:

usageTracking bean的支持类可以包含下面的方法:

public void (UsageTracked usageTracked) { usageTracked.incrementUseCount(); }
欲实现的接口由 implement-interface 属性来指定。 types-matching 属性的值是一个AspectJ类型模式:- 任何匹配类型的bean会实现 UsageTracked 接口。 注意在Before通知的例子中,srevice bean可以用作 UsageTracked 接口的实现。 如果编程形式访问一个bean,你可以这样来写:

UsageTracked usageTracked = (UsageTracked) context.getBean(“myService”);
6.3.5. 切面实例化模型
Schema-defined切面仅支持一种实例化模型就是singlton模型。其他的实例化模型或许在未来版本中将得到支持。

6.3.6. Advisors
“advisors”这个概念来自Spring1.2对AOP的支持,在AspectJ中是没有等价的概念。 advisor就像一个小的自包含的切面,这个切面只有一个通知。 切面自身通过一个bean表示,并且必须实现一个通知接口, 在 Section 7.3.2, “Spring里的通知类型” 中我们会讨论相应的接口。Advisors可以很好的利用AspectJ切入点表达式。

Spring 2.0 通过 元素来支持advisor 概念。 你将会发现它大多数情况下会和transactional advice一起使用,transactional advice在Spring 2.0中有自己的命名空间。格式如下:

和在上面使用的 pointcut-ref 属性一样,你还可以使用 pointcut 属性来定义一个内联的切入点表达式。

为了定义一个advisord的优先级以便让通知可以有序,使用 order 属性来定义 advisor的值 Ordered 。