zoukankan      html  css  js  c++  java
  • 后端——框架——切面框架——aop——aspect

      本篇介绍Aspect文件

    1、结构

      aspect的结构为

    [privileged] [access specification] [abstract] aspect <AspectName>
       [extends class-or-aspect-name] [implements interface-list]
       [<association-specifier> Pointcut] | [pertypewithin(TypePattern){
    		// aspect body
    }
    

      privileged关键字,可以访问类的私有字段。

      access specification,修饰符, public, protected, private等。

      abstract关键字,是否是抽象的aspect。

      aspect关键字,类似于Java类的class。

      aspect name,aspect的名称,通常以Aspect为后缀,例如XXAspect。

      extends class-or-aspect-name,继承,通常只能继承抽象的aspect。

      implements interface-list,可以实现接口。

      association-specifier pointcut,aspect association。之后再介绍。

      pertypewithin(TypePattern),是aspect association中的一种,它的含义与within(TypePattern)类似。

      aspect body,与普通的Java方法体相同,但是区别在于可以使用关键字,例如thisJoinPoint,proceed()。

    1.1     privileged

      默认情况下,aspect无法访问私有的字段和方法。若想访问,需要添加privileged。示例如下:

      Main对象

    public class Main {
         // 私有字段
         private int id;
    
         public static void main(String[] args) {
    	// 创建Main对象
    	Main main = new Main();
    	main.method1();
         }
    
         public void method1() {
    	 System.out.println("运行method1方法,私有字段id的值为:" + this.id);
        }
    }
    

      Aspect对象

    /**
     * 
     * @File Name: PrivilegeTestAspect.aj 
     * @Description: 创建Aspect
     * @version 1.0
     * @since JDK 1.8
     */
    privileged public aspect PrivilegeTestAspect {
    	
    	// 创建advice,使用匿名方式
    	before(Main callee) : call(void Main.method1()) && target(callee){
    		System.out.println("PrivilegeTestAsepct: before objectId="+ callee.id);
    	}
    }
    

    1.2    association

    默认情况下,aspect是单例的,全局的。以在aspect上定义association可以手动配置。

    Aspect有四种类型的aspect association

    1. Singleton(默认值)
    2. Per Object
    3. Per Control flow
    4. Per type

      1.2.1  singleton

    结构为:

    aspect <AspectName> issingleton(){
         // aspect body
    }
    

      它是默认值,issingleton可以省略。

    1.2.2   per object

      结构为:

    aspect <AspectName> perthis(pointcut) || pertarget(pointcut){
         // aspect body
    }
    

      当使用perthis时,此时join point的上下文必须存在this关键字,通常情况下是方法的execution,字段的读取和写入。

      当使用pertarget时,此时join point的上下文必须存在target关键字,通常情况下是方法的call。

      无论哪种形式,aspect实例与this或target指向的实例一一对应。

    引用原著中的内容:

    An aspect is created for each object when the join point matching the pointcut is executed the first time for that object

    每次匹配join point成功,都会创建aspect实例

    示例如下:

    // Account对象,只有debit 和 credit两个方法
    public class Account {
    
    	public void credit() {
    		System.out.println("调用Account对象的credit方法");
    	}
    
    	public void debit() {
    		System.out.println("调用Account对象的debit方法");
    	}
    }
    
    // Aspect, 配置Account两个方法的切面
    public aspect AssociationDemoAspect perthis(accountOperationExecution(Account)){
    	
    	// 实现构造器, 用于跟踪aspect实例的个数
    	public AssociationDemoAspect() {
    		System.out.println("正在创建AssociationDemoAspect");
    	}
    	/*
    	 * 定义pointcut
    	 * Account对象的credit方法或者是debit方法
    	 * 由于使用join point 为execution,所以收集实例时使用this关键字,当为调用时,使用target关键字
    	 */
    	pointcut accountOperationExecution(Account account) :(execution(* Account.credit(..)) || execution(* Account.debit(..))) && this(account);
    	
    	before(Account account) : accountOperationExecution(account){
    		System.out.println("JoinPoint: "+ thisJoinPointStaticPart + "
    	aspect:" + this + "
    	object :" +account);
    	}
    }
    
    // Main,测试
    public static void testAccount() {
    	// 创建Account1对象
    	Account account1 = new Account();
    	// 创建Account2对象
    	Account account2 = new Account();
    	
    	// 调用account1对象的credit方法
    	account1.credit();
    	// 调用account1对象的debit方法
    	account1.debit();
    	
    	// 调用account2对象的credit方法
    	account2.credit();
    	// 调用account2对象的debit方法
    	account2.debit();
    }
    

    1.2.3  per control flow

    结构为:

    aspect <AspectName> percflow(pointcut) || percflowbelow(pointcut){
         // aspect body
    }
    

      使用percflow时,等价于cflow,包含方法内部的所有join point及其自身。

      使用percflowbelow时,等价于cflowbelow, cflowbelow包含方法内部的所有join point,不包含该方法。

      无论哪种形式, aspect实例与方法调用一一对应。

      引用原著中的内容:

    Each instance is created just before the execution of the credit and debit methods, because a new control flow matching the pointcut specified starts with their execution

    每次调用credit和debit方法,都会创建一个aspect实例。

    示例同上

    1.2.4  per type

    结构为

    aspect <AspectName> pertypewithin(TypeSignature){
         // aspect body
    }
    

      它与per object类似,区别在于每一个对象实例对应一个aspect实例, per type是每一种类型对应一个aspect实例。

      当使用per type时,可以在Aspect中使用getWithinTypeName获取类名。

    引用原著中的内容:

    It’s typical to advise the static initializer to initialize the aspect state and use that state in other advice

    aspect实例的创建时机在类加载的过程, 对于每种类型来说,它的加载过程只有一次。

    2、特殊方法

    Aspect有两个静态方法,aspectOf,返回关联的aspect实例,若没有,则会创建新的aspect实例,并返回。

    因为Aspect对象不能通过new创建。实例的创建,以及内部方法的调用都是由系统自动完成的,如果需要访问aspect实例,可以调用Aspect.aspectOf方法。

    当aspect association的类型为per object时,aspectOf方法的参数为对象实例。若为per type时,参数为类对应的Class。

    hasAspect, 检查是否有实例与之关联。没有参数,返回值为布尔类型。

    3、优先级

    优先级的含义有两层,第一层是Aspect层级,即Aspect X与Aspect Y的优先级比较,第二层级是Advice层级,即在Aspect X中存在advice a, b, c, d等等,它们的优先级。

    优先级规则如下:

    The aspect with higher precedence executes its before advice on a join point before the aspect with lower precedence

    Before类型的advice,高优先级在低优先级之前运行

    The aspect with higher precedence executes its after advice on a join point after the aspect with lower precedence

    After类型的advice,高优先级在低优先级之后运行

    The around advice in the higher precedence aspect encloses the around advice in the lower precedence aspect

    Around类型的advice,高优先级在低优先级的外层。类似于方法的调用,越外层的方法越处于外层。与分析递归方法时的结构类似。例如f(n)阶层的递归方法,f(3)在f(2)的外层,f(2)在f(1)的外层,相当于f(3)是最大的盒子,f(2)是中等的盒子放入到f(3)的大盒子中,f(1)是最小的盒子放入到f(2)中。Around advice也是类似的,高优先级的advice对应大盒子,低优先级的advice对应小盒子。

    定义优先级语法的格式如下:

    declare precedence: aspect1, aspect2, aspect3 ….
    

      其中优先级按照aspect从左到右的顺序,越靠左边,前面的aspect具有越高的优先级。

      aspect的名称可以使用通配符*,例如auth*,* ;以auth为前缀的aspect比其他的aspect有更高的优先级。

      若aspect链出现重复,循环的情况时,定义顺序失败。例如 aspect1, aspect2, aspect1这种情况是错误的。auth*, aspect1, authAspect1这种情况也是错误。

      aspect链中的aspect不能是抽象的。即abstract修饰的aspect。

      若定义多条aspect链,彼此出现重复时,编译阶段不会发生错误,但是在运行时会出错。例如declare precedence : aspect1, aspect2; 之后又定义declare precedence aspect2, aspect1。编译时不会出错,当切面中的advice运行时,会抛错。

    Advice的优先级

    当同类型的多个advice对应同一个join point时,默认情况会根据语法顺序,advice定义越靠前, 优先级越高。

      当不同类型的advice对应同一个join point时,before类型的总是在join point之前执行,after类型总是在join point之后执行。around类型的advice,会在调用proceed()之前执行before类型,在调用之后执行after类型。proceed()之前的代码是在before之前执行的,proceed()之后的代码是在after之后执行的。

  • 相关阅读:
    左萧龙(LZ)个人博客
    不同样式的计数
    CSS径向渐变radial-gradient
    优秀的Android资源
    读取csv格式的数据
    php 获取URL 各部分参数
    phpstorm查找替换文件中的变量
    PhpStorm 快捷键大全 PhpStorm 常用快捷键和配置
    phpstorm 代码注释后,撤销某段代码的注释的,快捷键是什么?
    关于thinkphp5手动抛出Http异常时自定义404页面报错的问题
  • 原文地址:https://www.cnblogs.com/rain144576/p/14708706.html
Copyright © 2011-2022 走看看