AspectJ——切入点语法(4)之捕获属性上的连接点

捕获属性上的连接点

AspectJ提供了get(Signature)set(Signature)切入点的形式,来捕获可能发生在类属性上的任何访问和修改。这也是AspectJ的一个比较受争议的特性,因为它会有效地破坏类的封装性,特别是当把被监视的属性声明为protected或者private时。

所以这两个属性可以提供强大的手段来通知类,但是必须小心地使用它们。

0.捕获对属性的访问

我们使用get(Signature)切入点来捕获对对象属性的访问。切入点的语法如下:

pointcut [切入点名字](想要捕获的参数): get(<可选的修饰符> 属性类型 类名.属性名)

需要注意的几点是:

  1. get(Signature)切入点会捕获对属性的直接访问,不仅仅只会捕获对属性getter访问器方法的调用。
  2. get(Signature)切入点不能捕获对常量的访问。
  3. Signature必须解析成特定类的属性。
  4. Signature可以包含通配符,用于选择不同属性上的一系列连接点。

我们在Test7包下做一个简单的测试。首先新建业务类Service

package Test7;

public class Service {
    protected static String name = "Gavin John";
    public static final String nickname = "GG";
    private String firstname = "Gavin";
    private String lastname = "John";

    public String getFirstname() {
        return firstname;
    }

    public void setFirstname(String firstname) {
        this.firstname = firstname;
    }

    public String getLastname() {
        return lastname;
    }

    public void setLastname(String lastname) {
        this.lastname = lastname;
    }
}

可以看到,在Service类中,我们首先定义了静态的name属性,接着使用static final定义了常量nickname,然后又定义两个普通的变量firstnamelastname,并且为两个普通的变量提供了gettersetter方法。

接着我们定义Main主方法类,在主方法中访问这些变量:

package Test7;

public class Main {
    public static void main(String[] args) {
        Service service = new Service();
        System.out.println("service.getFirstname(): " + service.getFirstname());
        System.out.println("service.getLastname(): " + service.getLastname());
        System.out.println("Service.name: " + Service.name);
        System.out.println("Service.nickname: " + Service.nickname);
    }
}

如果我们不添加切面,此时的运行结果显而易见,如下:

这里写图片描述

接着,我们添加切面FieldAspect,如下:

package Test7;

public aspect FieldAspect {
    pointcut getNamePointcut(): get(String Service.*name);

    before():getNamePointcut(){
        System.out.println();
        System.out.println("Signature: " + thisJoinPoint.getSignature());
        System.out.println("Source Line: " + thisJoinPoint.getSourceLocation());
    }
}

在该切面中,我们使用get(Signature),并通过通配符定义了访问Service类中的以name结尾的属性切入点。

此时运行结果如下:

这里写图片描述

从运行结果中可以看出,除了对nickname属性的访问之外,对其他属性的访问都被捕获到。nickname是一个通过static final定义的常量,因为编译器对常量的编译特性,AspectJ无法捕获对常量的访问。

而我们对firstnamelastname属性的访问是通过其getter方法,而对name静态属性访问是直接通过属性名字访问,这两种访问形式都被切入点捕获到。

1.获取访问的属性值

我们不仅可以捕获对属性的访问,还能获取所访问的属性值。这就要通过after() returning(<ReturnValue>)形式的通知,它在声明的returning()部分中带有一个标识符,用于包含访问过的值。

比如,我们简单修改上例中的切面,将其中的before()前置通知,改为after() returning(<ReturnValue>)形式的后置通知,如下:

package Test7;

public aspect FieldAspect {
    pointcut getNamePointcut(): get(String Service.*name);

    after() returning(String value):getNamePointcut(){
        System.out.println();
        System.out.println("Signature: " + thisJoinPoint.getSignature());
        System.out.println("Source Line: " + thisJoinPoint.getSourceLocation());
        System.out.println("访问的属性值是:" + value);
    }
}

此时运行结果是:

这里写图片描述

可以看到,我们在通知中获取到了访问的属性值。

2.捕获对属性的修改

我们使用set(Signature)切入点来捕获对对象属性的修改。其语法是:

pointcut [切入点名字](要获取的参数): set(<可选的修饰符> 属性类型 类名.属性名)

需要注意的几点是:

  1. set(Signature)切入点在修改属性时触发。
  2. Signature必须解析成特定类的属性。
  3. Signature可以包含通配符,用于选择不同属性上的一系列连接点。

我们在Test8中做一个简单的测试。Test8包结构与上面的Test7一样。

这里写图片描述

Service类与上例中一样,这里不再赘述。测试类Main中,我们将对属性的访问修改为对属性的修改,如下:

package Test8;

public class Main {
    public static void main(String[] args) {
        Service service = new Service();
        Service.name = "Gavin0 John0";
        service.setFirstname("Gavin0");
        service.setLastname("John0");
    }
}

首先我们直接通过属性名字修改name属性。接着调用firstnamelastnamesetter方法。

接着添加切面FieldAspect,如下:

package Test8;

public aspect FieldAspect {
    pointcut setNamePointcut(): set(String Service.*name);

    before(): setNamePointcut(){
        System.out.println();
        System.out.println("Signature: " + thisJoinPoint.getSignature());
        System.out.println("Source Line: " + thisJoinPoint.getSourceLocation());
    }
}

切入点setNamePointcut使用set(Singature)和通配符定义了对Service类中以name结尾的属性的修改切入点。

运行程序,结果如下:

这里写图片描述

可以看到,切面捕获了对三个属性的修改,但是奇怪的是每个属性都修改了两遍,这是为什么呢?其实不难理解,在Service类中,我们对这三个属性的第一次赋值,也算是一次修改,这也被切面捕获了。

3.在修改属性时获取赋给它的值

假如我们想在通知中获取,修改属性的时候赋给属性的值,该怎么做呢?这可以结合我们前面用过的args原生切入点,获取给其传递的参数即可。

如上例,我们将切面修改如下:

package Test8;

public aspect FieldAspect {
    pointcut setNamePointcut(String newValue): set(String Service.*name) && args(newValue);

    before(String newValue): setNamePointcut(newValue){
        System.out.println();
        System.out.println("Signature: " + thisJoinPoint.getSignature());
        System.out.println("Source Line: " + thisJoinPoint.getSourceLocation());
        System.out.println("赋给属性的值是:" + newValue);
    }
}

此时的运行结果是:

这里写图片描述

猜你喜欢

转载自blog.csdn.net/gggavin/article/details/80231621