aop概念理解(5)

2019-08-29 00:30

些(不重要的), (3)移除不被使用的本地变量的优化功能将会失效。 换句话说,你在使用这个flag的时候不会遇到任何困难。

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

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

6.2.4.6.4. 处理参数

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

@Around(\

\ \

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(\ public void fooExecution() {}

@Before(\ public void doBeforeOne() { // ..

}

@Before(\ public void doBeforeTwo() { // .. }

@AfterReturning(\ public void doAfterOne() { // .. }

@AfterReturning(\ 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=\ defaultImpl=DefaultUsageTracked.class) public static UsageTracked mixin;

@Before(\ \

public void recordUsage(UsageTracked usageTracked) { usageTracked.incrementUseCount(); }

}实现的接口通过被注解的字段类型来决定。@DeclareParents 注解的 value 属性是一个AspectJ的类型模式:- 任何匹配类型的bean都会实现 UsageTracked 接口。 请注意,在上面的前置通知(before advice)的例子中,service beans 可以直接用作 UsageTracked 接口的实现。 如果需要编程式的来访问一个bean,你可以这样写:

UsageTracked usageTracked = (UsageTracked)

context.getBean(\切面实例化模型 这是一个高级主题...

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

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

@Aspect(\\

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(\

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配置如下:

class=\

为了改进切面,使之仅仅重试idempotent操作,我们可以定义一个 Idempotent 注解:

@Retention(RetentionPolicy.RUNTIME) public @interface Idempotent { // marker annotation

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


aop概念理解(5).doc 将本文的Word文档下载到电脑 下载失败或者文档不完整,请联系客服人员解决!

下一篇:java练习题+答案

相关阅读
本类排行
× 注册会员免费下载(下载后可以自由复制和排版)

马上注册会员

注:下载文档有可能“只有目录或者内容不全”等情况,请下载之前注意辨别,如果您已付费且无法下载或内容有问题,请联系我们协助你处理。
微信: QQ: