简体   繁体   English

super()调用Java API的切入点

[英]Pointcut for super() call to Java API

I'm trying to use AspectJ to hook calls into the Java API. 我正在尝试使用AspectJ将调用挂接到Java API中。 For example, let's say I have an aspect for java.io.File: 例如,假设我有一个java.io.File方面:

import java.io.File;

aspect FileTest {
  File around(String arg0): args(arg0) && call(public File.new(String)) {
    throw new RuntimeException("Example");
  }
}

This hooks calls to the File(String) constructor just fine. 这样就可以将调用挂钩到File(String)构造函数。 However it will not do anything for the following code: 但是,对于以下代码,它将不执行任何操作:

public class FileLoophole extends File {
    public FileLoophole(String filename) {
        super(filename);
    }
}

According to https://eclipse.org/aspectj/doc/next/progguide/language-joinPoints.html , I should use the execution() pointcut to handle super() calls instead. 根据https://eclipse.org/aspectj/doc/next/progguide/language-joinPoints.html的说明 ,我应该改用execute()切入点来处理super()调用。 However, this doesn't work because the execution point is in the Java API, which I can't weave code into. 但是,这不起作用,因为执行点位于Java API中,我无法将代码编织到其中。 Is there a pointcut to capture these super() callsites? 是否有捕获这些super()调用站点的切入点? Is there a way to do it without knowing about the FileLoophole class beforehand? 有没有办法事先不了解FileLoophole类的方法?

You basically have two options: 您基本上有两个选择:

  • Use the pattern File+ in order to match pointcuts including subclasses. 使用模式File+可以匹配包括子类在内的切入点。 There is no need to know their names. 无需知道他们的名字。
  • Use AspectJ binary (post-compile) weaving and inject your aspect code directly into the JDK classes from rt.jar, creating a modified version of it or just packaging the modified JDK classes into a new JAR and prepending it to the boot classpath. 使用AspectJ二进制(编译后)编织,将方面代码直接从rt.jar注入到JDK类中,创建它的修改版本,或者仅将修改后的JDK类包装到新的JAR中,然后将其添加到引导类路径中。

While the former approach is non-intrusive and independent of your ability to modify the JDK in your runtime environment, it is also indirect and not exactly what you asked for. 尽管前一种方法是非侵入性的,并且与您在运行时环境中修改JDK的能力无关,但它也是间接的,并且并非您所要求的。 The latter approach is what you asked for but probably not the thing you want to do except for very special cases. 后一种方法是您要的,但除了非常特殊的情况外,可能不是您想做的。

Driver application: 驱动程序应用程序:

package de.scrum_master.app;

import java.io.File;

public class FileLoophole extends File {
    public FileLoophole(String filename) {
        super(filename);
    }

    public static void main(String[] args) {
        new File("file.txt");
        new FileLoophole("loophole.txt");
    }
}

Aspect: 方面:

package de.scrum_master.aspect;

import java.io.File;

public aspect FileInterceptor {
    Object around(String fileName): call(File+.new(String)) && args(fileName) {
        System.out.println(thisJoinPoint + " -> " + fileName);
        return proceed(fileName);
    }

    void around(String fileName): execution(File+.new(String))  && args(fileName) {
        System.out.println(thisJoinPoint + " -> " + fileName);
        proceed(fileName);
    }
}

Console output: 控制台输出:

call(java.io.File(String)) -> file.txt
call(de.scrum_master.app.FileLoophole(String)) -> loophole.txt
execution(de.scrum_master.app.FileLoophole(String)) -> loophole.txt

PS: Please note that while call(*.new(..)) returns an object, execution(*.new(..)) does not, which is why the around() advice's return type is void . PS:请注意,尽管call(*.new(..))返回一个对象,但execution(*.new(..))却没有,这就是around()建议的返回类型为void These semantics are described in the AspectJ documentation . 这些语义在AspectJ文档中进行了描述。


Update: You asked about inner classes in your comment. 更新:您在评论中询问了内部类。 Well, my pointcut works for static inner classes without any change. 好吧,我的切入点适用于静态内部类,无需进行任何更改。 But a non-static inner class needs an instance of its surrounding class in its constructor. 但是,非静态内部类在其构造函数中需要其周围类的实例。 Check this out, I created a class + debug aspect for you: 检查一下,我为您创建了一个类+调试方面:

package de.scrum_master.app;

import java.io.File;

public class Application {
    private class FileLoophole extends File {
        public FileLoophole(String filename) {
            super(filename);
        }
    }

    public static void main(String[] args) {
        new File("file.txt");
        new Application().new FileLoophole("loophole.txt");
    }
}
package de.scrum_master.aspect;

public aspect FileInterceptor {
    before() : within(de.scrum_master.app.Application) {
        System.out.println(thisJoinPoint);
    }
}

Now look at the console log: 现在查看控制台日志:

staticinitialization(de.scrum_master.app.Application.<clinit>)
execution(void de.scrum_master.app.Application.main(String[]))
call(java.io.File(String))
call(de.scrum_master.app.Application())
preinitialization(de.scrum_master.app.Application())
initialization(de.scrum_master.app.Application())
execution(de.scrum_master.app.Application())
call(Class java.lang.Object.getClass())
call(de.scrum_master.app.Application.FileLoophole(Application, String))
staticinitialization(de.scrum_master.app.Application.FileLoophole.<clinit>)
preinitialization(de.scrum_master.app.Application.FileLoophole(Application, String))
initialization(de.scrum_master.app.Application.FileLoophole(Application, String))
execution(de.scrum_master.app.Application.FileLoophole(Application, String))

As you can see at the end of the log, an inner class's constructor is converted into something which takes the surrounding class instance as its first parameter, thus the mismatch. 正如您在日志末尾看到的那样,内部类的构造函数将转换为将周围的类实例作为其第一个参数的对象,从而导致不匹配。 Now, knowing that, we can change our original pointcut in order to capture all constructors: 现在,知道了这一点,我们可以更改原始切入点以捕获所有构造函数:

void around(): execution(File+.new(..)) {
    System.out.println(thisJoinPoint);
    proceed();
}

If you still want to capture the file name, it gets a little more complicated: 如果您仍然想捕获文件名,它将变得更加复杂:

void around(String fileName): execution(File+.new(*, String)) && args(*, fileName) {
    System.out.println(thisJoinPoint + " -> " + fileName);
    proceed(fileName);
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM