• 框架——切面框架——aop——@Aspect


      传统语法(aspect类)--->注解语法(@Aspect)之间的兼容性,以及映射关系。在实际应用场景中,传统语法基本不使用。

    1、aspect

    1.1 语法

      传统语法:

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

      注解语法:  

    @Aspect(["<perthis | pertarget | percflow | percflowbelow>(Pointcut) | [pertypewithin(TypePattern)]"])
    [access specificiation] [abstract] [static] class <AspectName>
        [extends class-or-aspect-name]
        [implements interface-list]{
        // aspect body
    }
    

      二者的映射:

    1. Aspect传统语法的修饰符映射为Java的修饰符,此时不支持Privileged修饰符。
    2. Aspect关键字映射为@Aspect注解
    3. Aspect-name映射为Java的类名
    4. Extends | implements:没有变化,但是规则上需要Aspect传统语法的约束。
    5. association specifier: 映射为@Aspect注解的value属性
    6. pertypewithin:映射为@Aspect注解的value属性,与association specifier只能出现一种。

      注意事项:

    • 有@Aspect注解的Java类,被视为Aspect,此时Java类不能是接口,枚举类,注解类等
    • Java类必须有无参构造器,一般也是通过系统去调用,不要尝试自己去new
    • Aspect只能继承抽象的Aspect,与Java的继承规则不同
    • Aspect类上不能拥有泛型参数,除非是抽象的Aspect

    1.2  方法

    传统语法:

    XXAspect.aspectOf, XXAspect.hasAspect()

    注解语法:

    Aspects.aspectOf(XXAspect.class),Aspects.hasAspect().

    1.3  优先级

    传统语法:

    declare precedence : aspect1, aspect2..,可以有多条语句

    注解语法:

    @DeclarePredence(“aspect1, aspect2”),且只能有单个注解。

    2、pointcut

    2.1  abstract

    传统语法:

    [access specifier]  abstract pointcut <pointcutName>(args)

    注解语法:

    @Pointcut

    [access specifier] abstract void <methodName >(args)

    二者的映射

    1. pointcut关键字转换为@Pointcut注解,它是标注在方法上的
    2. pointcut的名称转换为Java方法的名称。

      注意事项:Java方法的修饰符不能是private 

    2.2  concrete

    传统语法:

    [access specifier] pointcut <pointcutName> : (kinded pointcut) && (non-kinded pointcut)

    注解语法:

    @Pointcut(“<pointcut-definition>”)

    [access specifier] pointcut <methodName>(args){}

    在abstract的基础上,还包含:

    1. Kinded pointcut映射为@Pointcut的value属性,即<pointcut-definition>部分
    2. Non-kinded pointcut收集的上下文映射为方法的参数,顺序由@Pointcut的argNames属性指定。
    @Pointcut(value = "execution(* ch6.Account(..)) && this(account) && args(amount)", argNames = "account,amount")
    public void test(Account account, String amount) {
    } 
    

      注意事项:在编写类签名时,需要使用全类名, import语法无用。

    2.3   If

    If有三种形式,是if(true),if(false),if(),

    if(true)等价于没有。

    if(false)等价于否定所有的pointcut。

    If()形式下,方法的返回值类型为布尔,它类似于Java语法中if(expression),方法的修饰符必须是public static。

    3、advice

    3.1  common

    3.1.1   注意事项

    无论是注解方式,传统方式,都需要遵循advice自身的规则,

    1. advice自身的约束,例如before, after没有返回值。不能手动调用,由系统自动调用等。
    2. Advice映射的方法虽然是Java的方法,在选择Join point时,需要将其视为Advice方法对待。例如选择@Before标注的方法时,使用adviceexecution(),而不是call 或 execution。

    3.1.2   上下文

    传统语法:使用this(), target()收集上下文并传递。

    注解语法:相同,需要指定参数顺序。

    3.1.3   特殊关键字

    传统语法:直接在advice中可以使用thisJoinPoint, thisJoinPointStaticPart。

    注解语法:使用thisJoinPoint需添加JoinPoint参数,使用thisJoinPointStaticPart需添加JoinPoint.staticPart参数,使用thisEnclosingJoinPointStaticPart时,添加JoinPoint.EnclosingStaticPart参数。

    3.2  before

    传统语法:

    before({arguments}) :pointcut({arguments}) {
      // advice body
    }
    

      注解语法:

    @Before(“<pointcut>”)
    public void <advice-name> ({arguments})
    

      二者的映射关系为:

    1. before映射为@Before注解
    2. pointcut映射为@Before注解的value属性
    3. arguments代表收集到的上下文信息,传统方式下,上下文信息首先收集到pointcut,之后映射为before()中的参数。注解方式下,上下文信息直接映射为方法的参数。
    4. 由于before advice没有返回值,映射为void。

      注意事项: 必须使用public,void修饰

    3.3 after

      After有三种类型,after finally, after returning, after throwing。

    After finally与before类型的唯一区别是前者使用@After注解,后者使用@Before注解。

    After returning与before类型的advice区别有两点,

    1. 注解不同,前者使用@AfterReturning注解,后者使用@Before注解
    2. After returning可以收集方法的返回值,若方法的返回值类型为特定类型时,相当于添加了返回值类型的条件,只会选择返回值类型匹配的方法。具体的方式为设置注解的returning属性,它的值为方法的返回值,不指定类型的情况下,默认为Object。

    After throwing与After returning的区别有以下两点

    1. 注解不同,前者使用@AfterThrowing,后者使用@AfterReturning。
    2. After throwing收集的是异常类型,after returning收集的是返回值。此时设置注解的throwing属性,它的值为抛出的异常实例,不指定类型的情况下,默认为Exception  

    3.4   around

    around类型的advice与before, after类型的advice复杂一些,主要体现在两个方面。

    第一个方面proceed关键字的使用。

    第二个方面上下文收集时,需要收集的信息比before和after复杂。

    对于before, after类型的advice,它们无法使用proceed关键字,该关键字代表调用原始的方法,在传统方式下,可以直接在advice body中调用proceed。在注解方式下,需要在方法上添加ProceedingJoinPoint参数,通过该对象的实例调用proceed方法。

    @Around(value ="noArgPointcut()")
    public Object testAround(ProceedingJoinPoint pjp) {
    	// 无参方式调用
    	pjp.proceed();
    	// 有参数方式调用, 参数为Object[]数组
    	Object proceed = pjp.proceed(new Object[5]);
    	return proceed;
    }
    

      在收集上下文时,

    1. 当为execution时,使用this收集当前方法中this关键字指向的对象
    2. 当为call时,使用target收集当前方法的调用者
    3. 默认情况下,join point中方法的参数会被收集为Object[],并作为around方法的参数。

    4、ITD

    4.1  类层次结构

      传统语法:

    declare parents : (TypePattern) implements (interfaceList)
    declare parents : (TypePattern) extends (Class or InterfaceList)
    

      注解语法:

    @DeclareMixin(“typePattern”)
    public XXInterface methodName(){
        return XXInterfaceImpl;
    }
    

      映射关系为:

    1. 注解方式下无extends类型的对应语法。
    2. Declare parents关键字映射为DeclareMixin注解或者是DeclareParents注解。
    3. Type Pattern映射为注解的value属性。
    4. interfaceList映射为XXInterface。若XXInterface继承了其他Interface,例如A, B, C,可以使用@DeclareMixin的interfaces属性指定接口的范围,例如interfaces属性值为A时,只需要实现XXInterface,A接口。B,C接口被忽略。

    示例如下:

    /**
    * @Title: testDeclareParent
     * @Description:与declare parents : ch5.*Customer implements BeanSupport等价
     *  1、ch5.*Customer对应value属性
     *  2、BeanSupport对应方法的返回值
     *  3、BeanSupport中默认的static aspect Impl代码块(默认接口实现)对应BeanSupportImpl中的默认实现代码块
     *  4、declare parents对应@DeclareMixin注解
    */
    @DeclareMixin(value = "ch5.*Customer")
    public BeanSupport testDeclareParent() {
    	return new BeanSupportImpl();
    }
    

    4.2  error && warning

    传统方式下,它的语法结构为:

    declare error : <pointcut> : <message>
    declare warning : <pointcut> : <message>
    

      注解方式下,它的语法结构为:

    @DeclareError("pointcut”)
    static final String varName = message
    

      二者的映射关系为:

    1. declare error转换为@DeclareError注解
    2. pointcut转换为注解的value属性
    3. message转换为变量的值。

    注意事项:

    1. 变量名称可以随意,但是变量的值无法改变,所以需要使用final修饰。
    2. message的值必须是字面量,不允许是方法的返回值,或者是字符串拼接中存在方法的返回值。
    3. 变量必须使用static修饰,确保不执行任何方法或构造器的情况下便可以获取变量的值。

    4.3  不支持

    给类,方法,构造器等添加注解不支持。

    给类添加新成员不支持。

    忽略必检异常不支持。

    5、对比

    1. 传统方式需要学习Aspect语法,而注解方式下兼容Java语法。在编程习惯方面注解方式更具有优势,但是传统方式是注解方式的基础,所以从学习难度上看,二者是等价的。
    2. 在传统方式下,在类签名时可以只使用类名,而在注解方式下,类签名中必须使用类全名。Java中的import无法在pointcut中使用
    3. 若只使用Advice,二者的功能是相同的。需要实现ITD时,传统方式比注解方式的功能更全面。
    4. 当使用Spring框架时,注解方式的兼容性更强。

    Privileged关键字不支持。

  • 相关阅读:
    实验六 进程基础
    实验五 shell脚本编程
    实验四 Linux系统搭建C语言编程环境
    实验三 Linux系统用户管理及VIM配置
    实验二 Linux系统简单文件操作命令
    实验一 Linux系统与应用准备
    实验八 进程间的通信
    实验七 信号
    实验六 进程基础
    实验五 shell脚本编程
  • 原文地址:https://www.cnblogs.com/rain144576/p/14729919.html
Copyright © 2020-2023  润新知