AspectJ——定义通知
Posted KLeonard
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了AspectJ——定义通知相关的知识,希望对你有一定的参考价值。
定义通知
切入点定义了你对哪些连接点感兴趣,通知则定义了当遇到这些连接点时要做什么。通过块包含直观的Java代码,它看起来非常像Java方法,只不过不能从应用程序中调用它。
0.前置通知
前面的大部分示例都使用了before()
前置通知,它在触发它的连接点之前执行通知。关于它的用法这里不再赘述。这里只贴一个之前的切面例子:
package Test10;
public aspect WithincodeAspect
pointcut withinCodePointcut(): withincode(* Main.main(*));
before(): withinCodePointcut()
System.out.println();
System.out.println(thisJoinPoint.getKind());
System.out.println("Signature: " + thisJoinPoint.getSignature());
System.out.println("Source Line: " + thisJoinPoint.getSourceLocation());
1.环绕通知
around()
环绕通知在触发它的连接点周围执行通知。它是一种强大的构造,它指示AspectJ应该运行通知,而不是指示连接点触发它。这允许它重写应用程序中的原始逻辑。
我们在Test12
包下做测试,首先创建Service
类,该类具有三个方法。如下:
package Test12;
public class Service
public int add(int a, int b)
return a + b;
public double square(double a)
return a * a;
public String upper(String string)
return string.toUpperCase();
三个方法分别是加法运算、平方运算以及将字符串转换成全大写的运算。
接着我们在主方法中测试它,Main
类如下:
package Test12;
public class Main
public static void main(String[] args)
Service service = new Service();
System.out.println("service.add(1, 2) = " + service.add(1, 2));
System.out.println("service.square(6) = " + service.square(6));
System.out.println("service.upper(\\"Gavin\\") = " + service.upper("Gavin"));
如果不添加任何切面,此时的运行结果如下:
结果正如我们所料,完全正确。
此时我们添加切面AroundAspect
,如下:
package Test12;
public aspect AroundAspect
pointcut addPointcut(): call(* add(..));
pointcut squarePointcut(double value): call(* square(double)) && args(value);
pointcut upperPointcut(String value): call(* upper(String)) && args(value);
int around():addPointcut()
System.out.println(thisJoinPoint + " 执行之前...");
int result = proceed();
System.out.println("结果是:" + result);
System.out.println(thisJoinPoint + " 执行之后...");
return result;
double around(double value): squarePointcut(value)
System.out.println();
System.out.println("接收到的参数是:" + value);
return proceed(value * 2);
String around(String value): upperPointcut(value)
System.out.println();
System.out.println("接收到的参数是:" + value);
return value;
该切面分别针对Service
类中的三个方法调用定义了三个切入点,并且使用around()
为这三个切入点定义了环绕通知。
在通知体内,proceed()
方法表示执行原来的方法(也就是被切入的方法)。对于addPointcut
,我们直接调用了无参数的proceed()
方法,它表示按照原来的参数执行原来的方法,结果是不变的。对于squarePointcut
,我们接收了原来的参数,并且调用了proceed(value * 2)
,将原来的参数扩大两倍再传给原来的方法,很显然,结果被我们改变了。而对于upperPointcut
,我们干脆就没有调用proceed()
方法,直接返回了一个值给它。
执行结果如下:
对比之前的运行结果,我们发现现在的结果完全改变了。这充分证明了,around()
环绕通知可以改变原来方法的运行逻辑。
需要注意的是:
around()
通知必须具有执行的返回值,如果不需要值,那么它可以是void
的。- 使用
around()
通知时,性能是需要考虑的事项。在AspectJ中使用around()
通知会有性能损耗,有可能的话,优先考虑结合使用before()
和after()
通知。
2.后置通知
后置通知包括三种:
- 普通的后置通知,其在连接点之后无条件执行通知。
- 成功返回的后置通知,其必须从连接点成功返回,才会在该连接点之后执行通知。
- 异常返回的后置通知,连接点出现异常,才会在该连接点之后执行通知。
2.0.普通的后置通知
普通的后置通知即after()
通知,其无论连接点是成功返回还是异常返回,都会执行通知。它与before()
相对应,一个在前,一个在后而已。关于它的使用,这里不再赘述。这里只贴一个之前的切面例子:
package Test6;
public aspect ConstructorAspect
pointcut staticInitializationPointcut(): staticinitialization(SuperService+);
after(): staticInitializationPointcut()
System.out.println();
System.out.println("staticInitializationPointcut在这里...");
System.out.println("Signature: " + thisJoinPoint.getSignature());
System.out.println("Source Line: " + thisJoinPoint.getSourceLocation());
2.1.成功返回的后置通知
成功返回的后置通知,仅在特定的连接点成功返回时,才会在该连接点之后执行通知。其使用after() returning
,如果你想接收连接点的返回值,也可以使用after() returning(变量类型 变量名字)
。
比如下述切面,使用了成功返回的后置通知:
public aspect TestAspect
pointcut afterPointcut(): call(* add(..));
after() returning: afterPointcut()
System.out.println("Signature: " + thisJoinPoint.getSignature());
System.out.println("Source Line: " + thisJoinPoint.getSourceLocation());
在前面的例子中,我们对属性的获取,即get
切入点使用了后置通知,并在通知中使用了获取到的属性值。如下:
package Test7;
public aspect FieldAspect
pointcut getNamePointcut(): get(String Service.*name);
after() returning(String value):getNamePointcut()
System.out.println();
System.out.println("Signature: " + thisJoinPoint.getSignature());
System.out.println("Source Line: " + thisJoinPoint.getSourceLocation());
System.out.println("访问的属性值是:" + value);
2.2.异常返回的后置通知
异常返回的后置通知仅当特定的连接点引发了一个异常时,才会在该连接点之后执行通知。其使用after() throwing
,或者如果你想在通知中使用异常对象,也可以使用after() throwing(异常类型 变量名字)
来接收异常对象。
下述切面使用了异常返回的后置通知:
public aspect TestAspect
pointcut afterPointcut(): call(* add(..));
after() throwing: afterPointcut()
System.out.println("Signature: " + thisJoinPoint.getSignature());
System.out.println("Source Line: " + thisJoinPoint.getSourceLocation());
以上是关于AspectJ——定义通知的主要内容,如果未能解决你的问题,请参考以下文章