一. 捕获方法调用
使用call(Signature)切入点。其语法:
pointcut <pointcut name>(
<any values to be picked up>) :
call (
<optional modifier> <return type> <class>.<method>(<paramater type>));
1. 在方法调用上触发通知,其环境是调用类。
2. Signature可以包含通配符,用于选择不同类和方法上的一系列连接点。
具有通配符的签名 |
描述 |
* void MyClass.foo(int , float) |
无论修饰符是什么,都会捕获方法上的连接点。也可以忽略修饰符的可见性来做到这一点。 |
void MyClass.foo(int , float) |
* * MyClass.foo(int , float) |
无论修饰符或返回类型是什么,都会捕获方法上的连接点。 |
* MyClass.foo(int , float) |
* * *.foo(int , float) |
无论修饰符,返回类型或类是什么,都会捕获方法上的连接点。 |
* *.foo(int , float) |
* * *.*(int , float) |
无论修饰符,返回类型,类或者方法是什么,都会捕获方法上的连接点。 |
* * *.*(* , float) |
无论修饰符,返回类型,类,或者其中的参数包含任何内容并且后接一个浮点数的方法是什么,都会捕获方法上的连接点。 |
* * *.*(* , ..) |
无论修饰符,返回类型,类,或者其中的参数包含至少一个单值并且后接任意数量的方法是什么,都会捕获方法上的连接点。 |
* * *.*(..) |
无论修饰符,返回类型,类,或者其中有任意数量参数的方法是什么,都会捕获方法上的连接点。 |
* *(..) |
* mypackage..*.*(..) |
捕获mypackage包和子包内的任何方法上的连接点。 |
* MyClass+.*(..) |
捕获MyClass和任何子类中任何方法上的连接点。 |
我们来看一个最简单的例子:
package com.aspectj;
public aspect CallRecipe {
/**//*
* Specifies calling advice whenever a mehtod
* matching the following rules gets called:
*
* Class Name: MyClass
* Method Name: foo
* Method Return Type: void
* Method Parameters: an int followed by a String
*/
pointcut callPointCut() : call(void MyClass.foo(int , String));
//Advice declaration
before() : callPointCut() {
System.out.println("------------------- Aspect Advice Logic -------------------");
System.out.println("In the advice attached to the call point cut");
System.out.println("Actually executing before the point cut call");
System.out.println("But that's a recipe for Chapter 6!");
System.out.println("signature: " + thisJoinPoint.getStaticPart().getSignature());
System.out.println("Source Line: " + thisJoinPoint.getStaticPart().getSourceLocation());
System.out.println("------------------------------------------");
}
}
这样的一个方面可能执行下来的结果是这样的:
------------------- Aspect Advice Logic -------------------
In the advice attached to the call point cut
Actually executing before the point cut call
But that's a recipe for Chapter 6!
signature: void com.aspectj.MyClass.foo(int, String)
Source Line: MyClass.java:10
-----------------------------------------------------------
foo(int , String)
这里有一个报告需要提醒一下。我们先运行如下代码:
package com.aspectj;
public class Test extends MyClass{
public void foo(int age , String name) {
System.out.println("foo(int , String)");
}
public static void main(String[] args) {
Test c = new Test();
c.foo(3 , "name");
}
}
Test.java是MyClass.java的子类,并重写了foo(int , String)方法。按照Java正常的执行方法,运行Test.java是不会牵涉到任何方面的,但事实相反,callPointCut()通知还是会被执行。这就是AspectJ设计的比较妖怪的地方。对此,http://www.eecs.ucf.edu/~leavens/FOAL/papers-2004/barzilay-etal.pdf 有详细的描述。有兴趣的朋友可以研究一把。
/Files/zhengzhili/Call_and_Execution_Semantics_in_AspectJ.pdf
二. 捕获方法调用上传递的参数值
可以使用call(Signature)和args([TypePatterns | Identifiers])切入点来捕获对方法的调用,然后把需要的标识符绑定到方法的参数值上。
package com.aspectj;
public aspect CaptureCallPamaterRrecipe {
/**//*
* Specifies calling advice whenever a mehtod
* matching the following rules gets called:
*
* Class Name: MyClass
* Method Name: foo
* Method Return Type: void
* Method Parameters: an int followed by a String
*/
pointcut captureCallPamaters(int value , String name) : call(void MyClass.foo(int , String)) && args(value , name);
//Advice declaration
before(int value , String name) : captureCallPamaters(value , name) {
System.out.println("------------------- Aspect Advice Logic -------------------");
System.out.println("In the advice attached to the call point cut");
System.out.println("Captured int parameter on method: " + value);
System.out.println("Captured String parameter on method: " + name);
System.out.println("------------------------------------------");
}
}
上例中个人理解为两次绑定过程。第一次captureCallPamaters通过args([Types | Identifiers])将foo方法的参数绑定到自己的参数上;第二次再把自己的参数绑定到before()上。也因此,1. pointcut captureCallPamaters(int value , String name) : call(void MyClass.foo(int , String)) && args(value , name); 中参数名必须一一对应;2.before(int value , String name) : captureCallPamaters(value , name) 中参数名也必须一一对应。而1和2之间的参数名则不需要一一对应。
三. 捕获方法调用的目标
使用call(Signature)和targer([Type | Identifier])切入点来捕获方法的调用,然后把单一标识符绑定到正在调用方法的对象上。
package com.aspectj;
public aspect CaptureCallTargetRecipe {
/**//*
* Specifies calling advice whenever a method
* matching the following rules gets called:
*
* Class Name: MyClass
* Method Name: foo
* Method Return Type: void
* Method Parameters: an int followed by a String
*/
pointcut captureCallTarget(MyClass myObject) : call(void MyClass.foo(int , String)) && target(myObject);
//Advice declaration
before(MyClass myObject) : captureCallTarget(myObject) {
System.out.println("------------------- Aspect Advice Logic -------------------");
System.out.println("In the advice attached to the call point cut");
System.out.println("Captured target object for the method call: " + myObject);
System.out.println("------------------------------------------");
}
}
四. 当执行一个方法时捕获它
使用execution(Signature)切入点。其语法如下:
pointcut <pointcut name>(<any values to be picked up>) :
execution(<optional modifier> <return type> <class>.<method>(<paramater types>));
execution(Signature)切入点具有两个关键特征:
1。触发连接点的环境在目标类方法中。
2。Signature可以包含通配符,以选择不同类和方法上的一系列连接点。
package com.aspectj;
public aspect ExecutionRecipe {
/**//*
* Specifies calling advice whenever a method
* matching the following rules gets called:
*
* Class Name: MyClass
* Method Name: foo
* Method Return Type: void
* Method Parameters: an int followed by a String
*/
pointcut executionPointcut() : execution(void MyClass.foo(int , String));
//Advice declaration
before() : executionPointcut() && !within(ExecutionRecipe +) {
System.out.println("------------------- Aspect Advice Logic -------------------");
System.out.println("In the advice picked by ExecutionRecipe");
System.out.println("signature: " + thisJoinPoint.getStaticPart().getSignature());
System.out.println("Source Line: " + thisJoinPoint.getStaticPart().getSourceLocation());
System.out.println("------------------------------------------");
}
}
上述代码和第一部分所使用的call(Signature)切入点相比,没什么新的内容。但请注意是什么地方调用通知,以及它的环境是什么。即请特别关注thisJoinPoint.getStaticPart().getSourceLocation()的返回值。
五. 在执行方法时捕获this引用的值
在执行期间捕获方法时,想通过显示Java的this引用所指向的对象,使之可以被通知使用,可以使用execute(Signature)和this(Type | Identifier)切入点来捕获方法的执行,并把单一标识符绑定到方式执行期间this引用所指向的对象。
package com.aspectj;
public aspect CaptureThisReferenceRecipe {
/**//*
* Specifies calling advice whenever a mehtod
* matching the following rules gets called:
*
* Class Name: MyClass
* Method Name: foo
* Method Return Type: void
* Method Parameters: an int followed by a String
*/
pointcut captureThisDuringExecution(MyClass myObject) :
execution(void MyClass.foo(int , String)) && this (myObject);
//Advice declaration
before(MyClass myObject) : captureThisDuringExecution(myObject) {
System.out.println("------------------- Aspect Advice Logic -------------------");
System.out.println("In the advice attached to the execute point cut");
System.out.println("Captured this reference: " + myObject);
System.out.println("------------------------------------------");
}
}