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 属性来实现。示例如下:
pointcut=\@annotation(auditable)\ method=\
arg-names=\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。)
types-matching=\ implement-interface=\
default-impl=\
pointcut=\ and this(usageTracked)\ method=\
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(\切面实例化模型
Schema-defined切面仅支持一种实例化模型就是singlton模型。其他的实例化模型或许在未来版本中将得到支持。
6.3.6. 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中有自己的命名空间。格式如下:
expression=\
pointcut-ref=\ advice-ref=\
和在上面使用的 pointcut-ref 属性一样,你还可以使用 pointcut 属性来定义一个内联的切入点表达式。
为了定义一个advisord的优先级以便让通知可以有序,使用 order 属性来定义 advisor的值 Ordered 。
6.3.7. 例子
让我们来看看在 Section 6.2.7, “例子” 提过乐观锁失败重试的例子,如果使用schema对这个例子进行重写是什么效果。
因为乐观锁的关系,有时候business services可能会失败(有人甚至在一开始运行事务的时候就失败了)。 如果重新尝试一下,很有可能就会成功。对于business services来说,重试几次是很正常的(Idempotent操作不需要用户参与,否则会得出矛盾的结论) 我们可能需要透明的重试操作以避免让客户看见 OptimisticLockingFailureException 例外被抛出。 很明显,在一个横切多层的情况下,这是非常有必要的,因此通过切面来实现是很理想的。
因为我们想要重试操作,我们会需要使用到环绕通知,这样我们就可以多次调用proceed()方法。 下面是简单的切面实现(只是一个schema支持的普通Java 类):
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; }
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 异常,我们只需要简单的重试,直到我们耗尽所有预设的重试次数。
这个类跟我们在@AspectJ的例子中使用的是相同的,只是没有使用注解。 对应的Spring配置如下:
expression=\
pointcut-ref=\ method=\
class=\
@Retention(RetentionPolicy.RUNTIME) public @interface Idempotent { // marker annotation
}并且对service操作的实现进行注解。这样如果你只希望改变切面使得idempotent的操作会尝试多次,你只需要改写切入点表达式,这样只有 @Idempotent 操作会匹配:
expression=\ @annotation(com.xyz.myapp.service.Idempotent)\混合切面类型
我们完全可以混合使用以下几种风格的切面定义:使用自动代理的@AspectJ 风格的切面,schema-defined 的切面,和用 声明的advisor,甚至是使用Spring 1.2风格的代理和拦截器。 由于以上几种风格的切面定义的都使用了相同的底层机制,因此可以很好的共存。
6.5. 代理机制
Spring AOP部分使用JDK动态代理或者CGLIB来为目标对象创建代理。(建议尽量使用JDK的动态代理)
如果被代理的目标对象实现了至少一个接口,则会使用JDK动态代理。所有该目标类型实现的接口都将被代理。若该目标对象没有实现任何接口,则创建一个CGLIB代理。
如果你希望强制使用CGLIB代理,(例如:希望代理目标对象的所有方法,而不只是实现自接口的方法)那也可以。但是需要考虑以下问题:
无法通知(advise)Final 方法,因为他们不能被覆写。
你需要将CGLIB 2二进制发行包放在classpath下面,与之相较JDK本身就提供了动态代理