一.访问类成员
public privileged aspect MemberAccessRecipe {
/** *//**
* Specifies calling advice whenever a method
* matching the following rules gets executed:
*
* Class Name: MyClass
* Method Name:foo
* Method Return Type:void
* Method Parameters:an int followed by a String
*/
pointcut executionOfFooPointCut() : execution(void MyClass.foo(int,String));
//Advice declaration
after(MyClass myClass):executionOfFooPointCut() && this(myClass){
System.out.println("---------- Aspect Advice Logic ----------");
System.out.println("Accessing the set(float) member of the MyClass object");
System.out.println("Privileged access not required for this method call as it is public");
myClass.setF(2.0f);
System.out.println("Using the privileged aspect access to the private f member variable");
System.out.println("The current value of f is: ");
System.out.println(myClass.f);
System.out.println("Signature: " + thisJoinPoint.getSignature());
System.out.println("Source Line: " + thisJoinPoint.getSourceLocation());
System.out.println("-----------------------------------------");
}
}
通过使用this(Identifier)切入点定义,使得MyClass类的对象可供通知使用。this(Identifier)切入点定义有效地把通知展示给被触发连接点处的this引用指向的对象。从通知内调用setF(float)方法,并显示对MyClass对象的公共方法的访问。为了获得对MyClass.f私有属性的访问,方面不得不对其结构执行一些额外的更改。方面尝试通过直接访问私有成员来破快封装性,因此,必须把方面声明为privileged,因为它正在提交潜在的侵入动作。
AspectJ提供了privieged关键字,它用在方面需要完全不受限制地访问类的地方,包括那些未在类的公共接口上声明的成员变量和方法。方面的privileged状态应该充当一个警告,在对方面或者它所应用的类执行任何更改时,必须当心这个警告,因为这些更改可能潜在地在整个应用程序中引发其他问题。
二.访问连接点环境
AspectJ提供了thisJoinPoint变量,用于展示this连接点环境。如果可以静态地访问正在访问的环境,那么thisJoinPointStaticPart也是有用的。
public aspect ThisJoinPointRecipe {
/** *//**
* Specifies calling advice whenever a method
* matching the following rules gets executed:
*
* 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()&&!within(ThisJoinPointRecipe+){
System.out.println("---------- Aspect Advice Logic ----------");
System.out.println("Exercising the static parts of AspectJ 1.1.1 thisJoinPoint");
System.out.println("Source Line: " + thisJoinPointStaticPart.getSourceLocation());
System.out.println("JoinPoint Kind: " + thisJoinPointStaticPart.getKind());
System.out.println("Simple toString: " + thisJoinPointStaticPart.toString());
System.out.println("Simple toShortString: " + thisJoinPointStaticPart.toShortString());
System.out.println("Simple toLongString: " + thisJoinPointStaticPart.toLongString());
System.out.println("Exercising the join point generic signature of AspectJ 1.1.1 thisJoinPoint");
System.out.println("Signature name:" + thisJoinPointStaticPart.getSignature().getName());
System.out.println("Signature declaring type:" + thisJoinPointStaticPart.getSignature().getDeclaringType());
System.out.println("-----------------------------------------");
}
before():callPointCut()&&!within(ThisJoinPointRecipe+) {
System.out.println("---------- Aspect Advice Logic ----------");
System.out.println("Exercising the dynamic parts of AspectJ 1.1.1 thisJoinPoint");
System.out.println("Get the this reference: " + thisJoinPoint.getThis());
System.out.println("Getting the Target: " + thisJoinPoint.getTarget());
System.out.println("Join Point Arguments:");
Object[] args = thisJoinPoint.getArgs();
for (int count = 0; count < args.length ; count++) {
System.out.println(args[count]);
}
System.out.println("-----------------------------------------");
}
}
thisJoinPoint变量包含关于触发连接点的静态和动态环境信息。静态环境信息包含可以在编译和织入时决定的任何信息。动态连接点环境信息则只能在运行时填充,因为它依赖于连接点环境实际的运行时状态。
三.在连接点之前执行通知
使用before()类型的通知
public aspect BeforeAdviceRecipe {
/** *//**
* Specifies calling advice whenever a method
* matching the following rules gets executed:
*
* 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()&&!within(BeforeAdviceRecipe+) {
System.out.println("---------- Aspect Advice Logic ----------");
System.out.println("Source Location: " + thisJoinPoint.getStaticPart().getSourceLocation());
System.out.println("-----------------------------------------");
}
}
四.在连接点周围执行通知
使用around()类型的通知。
public aspect AroundAdviceRecipe {
/** *//**
* 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 callFooPointCut() : call(int MyClass.foo());
/** *//**
* Specifies calling advice whenever a method
* matching the following rules gets called:
*
* Class Name: MyClass
* Method Name:bar2
* Method Return Type:int
* Method Parameters:an int
*/
pointcut callBarPointCut(int value) : call(int MyClass.bar(int)) && args(value);
/** *//**
* Specifies calling advice whenever a method
* matching the following rules gets called:
*
* Class Name: MyClass
* Method Name:baz
* Method Return Type:int
* Method Parameters:
*/
pointcut callBazPointCut() : call(int MyClass.baz());
//Advice declaration
//This advice will be executed before the pointcut that picks it
int around() : callFooPointCut()&& !within(AroundAdviceRecipe+){
System.out.println("---------- Aspect Advice Logic ----------");
System.out.println("Signature: " + thisJoinPoint.getSignature());
System.out.println("Source Location: " + thisJoinPoint.getStaticPart().getSourceLocation());
System.out.println("-----------------------------------------");
return proceed();
}
//Advice declaration
//This advice will be executed before the pointcut that picks it
int around(int value):callBarPointCut(value)&&!within(AroundAdviceRecipe+){
System.out.println("---------- Aspect Advice Logic ----------");
System.out.println("Signature: " + thisJoinPoint.getSignature());
System.out.println("Source Location: " + thisJoinPoint.getStaticPart().getSourceLocation());
System.out.println("-----------------------------------------");
return proceed(value);
}
//Advice declaration
//This advice will be executed before the pointcut that picks it
int around(int value):callBarPointCut(value)&&!within(AroundAdviceRecipe+){
System.out.println("---------- Aspect Advice Logic ----------");
System.out.println("Signature: " + thisJoinPoint.getSignature());
System.out.println("Source Location: " + thisJoinPoint.getStaticPart().getSourceLocation());
System.out.println("-----------------------------------------");
return proceed(value);
}
//Advice declaration
//This advice will be executed before the pointcut that picks it
int around() : callBazPointCut() && !within(AroundAdviceRecipe+) {
System.out.println("---------- Aspect Advice Logic ----------");
System.out.println("Signature: " + thisJoinPoint.getSignature());
System.out.println("Source Location: " + thisJoinPoint.getStaticPart().getSourceLocation());
System.out.println("-----------------------------------------");
return 200;
}
}
around()通知是一种强大的构造,它指示AspectJ应该运行通知,而不是指示连接点触发它。这允许重写应用程序中存在的原始逻辑。这说明了around()通知,可以依据是否从around()通知块内发起proceed()调用,来被动或主动地应用这个通知。
proceed()调用指示around()通知应该继续执行原始连接点逻辑,并传递原来可用的任何值。
在示例中,第一份通知没有参数需要传递;第二份通知传递了一个与原始值完全不同的值;第三份通知则完全返回了一个不同的值。
around()通知必须具有指定的返回值,但是如果不需要值,那么可以是void。
五.在连接点之后无条件执行通知
使用after()类型的通知
public aspect AfterAdviceRecipe {
/** *//**
* 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 callPointCut() : call(void MyClass.foo(int,String));
//Advice declaration
after():callPointCut()&&!within(AfterAdviceRecipe+){
System.out.println("---------- Aspect Advice Logic ----------");
System.out.println("Source Location: " + thisJoinPoint.getStaticPart().getSourceLocation());
System.out.println("-----------------------------------------");
}
}
六.仅在从连接点正常返回之后才执行通知
使用after() returning或after() returning(<ReturnType> <Identifier>)类型的通知
public aspect AfterReturningAdviceRecipe {
/** *//**
* 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 callPointCut():call(void MyClass.foo(int));
//Advice declaration
after() returning:callPointCut()&&!within(AfterReturningAdviceRecipe+) {
System.out.println("---------- Aspect Advice Logic ----------");
System.out.println("Source Location: " + thisJoinPoint.getStaticPart().getSourceLocation());
System.out.println("-----------------------------------------");
}
}
public aspect AfterReturningValueAdviceRecipe {
/** *//**
* 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 callPointCut():call(void MyClass.foo(int));
//Advice declaration
after() returning(Object value):callPointCut()&&!within(AfterReturningAdviceRecipe+) {
System.out.println("---------- Aspect Advice Logic ----------");
System.out.println("Source Location: " + thisJoinPoint.getStaticPart().getSourceLocation());
System.out.println("Value being returned: " + value);
System.out.println("-----------------------------------------");
}
}
使用after() returning(<ReturnType> <Identifier>)通知访问基本类型的一个有趣作用是:必须把基本的int值装箱进Integer类的一个实例中,以传递给通知。当通知期待的返回类型是Object类型时,并且如果返回值是基本类型,AspectJ将自动并且透明地把基本值装箱进其对应的Java类中。around()形式的通知也可以使用这种自动和透明的装箱行为,其中会期待Object类型的值,并且将从通知传递基本值或将基本值传递给通知。
与正常的ater()相比,after() returning形式的通知提供了更精细的过滤器。
七.仅当连接点中引发了一个异常之后才执行通知
使用after() throwing或after() throwing(<ExceptionType> <Identifier>)类型的通知。
public aspect AfterThrowingAdviceRecipe {
/** *//**
* 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 callPointCut():call(void MyClass.foo(int));
//Advice declaration
after() throwing:callPointCut()&&!within(AfterThrowingAdviceRecipe+){
System.out.println("---------- Aspect Advice Logic ----------");
System.out.println("Source Location: " + thisJoinPoint.getStaticPart().getSourceLocation());
System.out.println("-----------------------------------------");
}
}
八.控制通知优先级
如果把位于不同方面中的相同类型的通知应用同一连接点,则可以使用declare precedence通知。其语法如下:
declare precedence:TypePattern,TypePattern,...;
public aspect AspectA {
// Declare precedence rules
declare precedence:AspectA,AspectB;
/** *//**
* 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 callPointCut():call(void MyClass.foo(int,String));
// Advice declaration
before():callPointCut()&&!within(AspectA+){
System.out.println("---------- Aspect Advice Logic ----------");
System.out.println("In the advice of AspectA");
System.out.println("Target: " + thisJoinPoint.getTarget());
System.out.println("This: " + thisJoinPoint.getThis());
System.out.println("Aspect Instance: " + AspectA.aspectOf());
System.out.println("-----------------------------------------");
}
}
在declare precedence语句中使用TypePattern来指定不同的方面,以及他们的显式次序。可以使用通配符来指定TypePatterns,以根据需要为特定的方面集合或方面的整个包指示优先级。
当把一个方面中的两个相同类型的通知块应用于同一个连接点时,declare precedence语句就没有意义了。为了处理这种情况,AspectJ基于方面声明内通知的类型和位置,来应用隐式的优先级次序:
- 按在方面中声明before()和around()通知类型的次序,来应用他们隐式优先级规则。如果把同一方面的两个before()通知块应用于同一个连接点,那么声明第一个块将具有最高的优先级,而最后一个则最低。
- 而after()、aftr() returning和around() throwing通知类型的隐式优先级则于before()相反。
九.通知方面
public aspect AdviseAspectRecipe {
/** *//**
* 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 callPointCut():call(void MyClass.foo(int,String));
//Advice declaration
before() : callPointCut()&&within(AdvisedAspect+) {
System.out.println("---------- Aspect Advice Logic ----------");
System.out.println("In the advice attached to the call point cut");
System.out.println("Signature: " + thisJoinPoint.getStaticPart().getSignature());
System.out.println("Source Line: " + thisJoinPoint.getStaticPart().getSourceLocation());
System.out.println("-----------------------------------------");
}
}