最爱Java

书山有路勤为径,学海无涯苦作舟

《AspectJ Cookbook》读书笔记十三: 定义通知

一.访问类成员 

package com.aspectj;

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也是有用的。

package com.aspectj;

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()类型的通知

 

package com.aspectj;

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()类型的通知。

package com.aspectj;

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()类型的通知

 

package com.aspectj;

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>)类型的通知

 

package com.aspectj;

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(
"-----------------------------------------");        
    }

}

 

package com.aspectj;

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>)类型的通知。

 

package com.aspectj;

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,...;

package com.aspectj;

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()相反。

九.通知方面

package com.aspectj;

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(
"-----------------------------------------");        
    }

}


posted on 2008-08-26 13:45 Brian 阅读(1554) 评论(1)  编辑  收藏 所属分类: 《AspectJ Cookbook》读书笔记

评论

# re: 《AspectJ Cookbook》读书笔记十三: 定义通知 2008-08-26 18:05 隔叶黄莺

AspectJ 确实很灵活,我很早也看过了这本书,只是实际中写 aj 文件的机会还没有,学到相关的知识可运用于 Spring 2.0 的 AspectJ 方式 AOP 相关配置。  回复  更多评论   


只有注册用户登录后才能发表评论。


网站导航:
 

公告


导航

<2008年8月>
272829303112
3456789
10111213141516
17181920212223
24252627282930
31123456

统计

常用链接

留言簿(4)

随笔分类

随笔档案

收藏夹

搜索

最新评论

阅读排行榜

评论排行榜