后端——框架——切面框架——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之后执行的。

原文地址:https://www.cnblogs.com/rain144576/p/14708706.html