AspectJ——切入点语法之限制连接点的作用域

Posted KLeonard

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了AspectJ——切入点语法之限制连接点的作用域相关的知识,希望对你有一定的参考价值。

限制连接点的作用域

在定义切入点的时候,我们经常遇到的需求是:基于所关注的程序作用域,限制捕获连接点的范围。

本节将介绍within以及withincode的用法。within可以指定切入点的作用域在包中或者类中,withincode可以通过方法签名限制连接点的作用域在方法中。

0.捕获特定类中的所有连接点

首先,我们使用within(TypePattern)切入点来捕获特定类中的所有连接点,它的语法如下:

pointcut [切入点名字](想要获取的参数): within(类名);

要注意的几点:

  1. within(TypePattern)切入点捕获指定类作用域中的所有连接点。
  2. within(TypePattern)切入点极少单独使用,它通常与其他切入点结合使用,用于减少将要捕获连接点。
  3. within(TypePattern)可以包含通配符,用于选择不同类上的一系列连接点。

我们在Test9包下进行测试。

首先创建业务类Service,如下:

package Test9;

public class Service 
    protected static String name = "Gavin John";
    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;
    

    public void test(int a, float b) 
        System.out.println("a + b = " + (a + b));
    

接着,创建测试类Main,进行测试:

package Test9;

public class Main 
    public static void main(String[] args) 
        Service service = new Service();
        System.out.println(service.getFirstname());
        System.out.println(service.getLastname());
        service.setLastname("Jack");

        service.test(3, 3.4F);
    

最后,我们创建切面WithinAspect,如下:

package Test9;

public aspect WithinAspect 
    pointcut withinService(): within(Service);

    before(): withinService()
        System.out.println();
        System.out.println(thisJoinPoint.getKind());
        System.out.println("Signature: " + thisJoinPoint.getSignature());
        System.out.println("Source Line: " + thisJoinPoint.getSourceLocation());
    

在该切面中,我们创建了withinService切入点,该切入点捕获所有在Service类中的连接点。并且为该切入点织入前置通知,在通知中我们打印了当前连接点的类型、签名以及在源代码中的位置。

运行结果如下:

由于Service类中的连接点非常多,所以这里只截取了一部分运行结果。从运行结果中可以看到,我们捕获了出现在Service类中的所有连接点,包含staticinitializationfield-setconstructor-execution等各种连接点。

1.捕获特定包中的所有连接点

在上例中,我们捕获了Service类中的所有连接点,现在假如我们要捕获Test9包下的所有连接点,该怎么做呢?

这里我们只需要简单修改切入点即可,如下,我们使用通配符表达式Test9.*来表示Test9包下的所有类。

package Test9;

public aspect WithinAspect 
    pointcut withinService(): within(Test9.*);

    before(): withinService()
        System.out.println();
        System.out.println(thisJoinPoint.getKind());
        System.out.println("Signature: " + thisJoinPoint.getSignature());
        System.out.println("Source Line: " + thisJoinPoint.getSourceLocation());
    

但是,如果我们此时运行程序,会得到出错的结果,如下:

报了NoAspectBoundException异常,这是为什么呢?这是因为,我们的切面WithinAspect也位于Test9包下,所以我们的切入点表达式Test9.*自然也包含了切面本身,也就是说切面也会捕获切面自身的切入点,并为其织入通知。

所以,在切面初始化的时候,切面初始化的连接点被捕获到,这时候要运行通知代码,而此时切面初始化并没有完成,通知自然也就不存在,所以就抛出了异常。简单来说,这个异常的原因就是,切面来横切并通知切面自身的时候出现了矛盾的情况。

我们通过!within(TypePattern)来将该切面排除在外即可,这里感叹号!表示逻辑非,即将其后面表达式的意义取反。如下:

package Test9;

public aspect WithinAspect 
    pointcut withinService(): within(Test9.*) && !within(WithinAspect);

    before(): withinService()
        System.out.println();
        System.out.println(thisJoinPoint.getKind());
        System.out.println("Signature: " + thisJoinPoint.getSignature());
        System.out.println("Source Line: " + thisJoinPoint.getSourceLocation());
    

within(Test9.*) && !within(WithinAspect)表示在Test9包下的所有类,但是排除了WithinAspect。此时可以成功运行。运行结果就是捕获了所有在Service类和Main类中的连接点。这里展示了一小部分结果:

2.捕获特定方法内的所有连接点

我们使用withincode(Signature)切入点来捕获与特定签名匹配的方法内的所有连接点。其语法是:

pointcut [切入点名字](要获取的参数): withincode(<可选的修饰符> 类名.方法名(参数列表));

要注意的几点是:

  1. withincode(Signature)切入点指定了特定方法作用域内的所有连接点。
  2. withincode(Signature)切入点很少单独使用。一般与其他切入点结合使用,用于减少将要捕获连接点。
  3. Signature可以包含通配符,用于选择跨越不同类不同方法上的一系列连接点。
  4. withincodewithin的区别就是withincode用于指定方法,而within用于指定类。

我们在Test10包下进行测试。

其中Service类与Main类与上例中的一样,这里不再赘述。我们在这里新建切面WithincodeAspect,如下:

package Test10;

public aspect WithincodeAspect 
    pointcut withinCodePointcut(): withincode(* Main.main(*));

    before(): withinCodePointcut()
        System.out.println();
        System.out.println(thisJoinPoint.getKind());
        System.out.println("Signature: " + thisJoinPoint.getSignature());
        System.out.println("Source Line: " + thisJoinPoint.getSourceLocation());
    

在该切面中,我们使用withincode(* Main.main(*))捕获了Main类中的main方法中的所有连接点。这里截取一部分运行结果:

以上是关于AspectJ——切入点语法之限制连接点的作用域的主要内容,如果未能解决你的问题,请参考以下文章

AspectJ——切入点语法之thistargetargsif以及逻辑运算

AspectJ——切入点语法之thistargetargsif以及逻辑运算

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

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

AspectJ——切入点语法之捕获方法上的连接点

AspectJ——切入点语法之cflow与cflowbelow