对Java API的super()调用的切入点

对Java API的super()调用的切入点,java,aop,aspectj,Java,Aop,Aspectj,我正在尝试使用AspectJ将调用挂接到JavaAPI中。例如,假设我有java.io.File的一个方面: import java.io.File; aspect FileTest { File around(String arg0): args(arg0) && call(public File.new(String)) { throw new RuntimeException("Example"); } } 这会很好地钩住对文件(字符串)构造函数的调用。

我正在尝试使用AspectJ将调用挂接到JavaAPI中。例如,假设我有java.io.File的一个方面:

import java.io.File;

aspect FileTest {
  File around(String arg0): args(arg0) && call(public File.new(String)) {
    throw new RuntimeException("Example");
  }
}
这会很好地钩住对文件(字符串)构造函数的调用。但是,它不会对以下代码执行任何操作:

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

根据,我应该使用execution()切入点来处理super()调用。但是,这不起作用,因为执行点在JavaAPI中,我无法将代码编入其中。是否有切入点来捕获这些super()调用站点?有没有一种方法可以在事先不知道FileLoophole类的情况下执行此操作?

您基本上有两种选择:

  • 使用模式
    File+
    匹配切入点,包括子类。没有必要知道他们的名字
  • 使用AspectJ二进制(编译后)编织,将方面代码直接从rt.jar注入JDK类,创建修改后的版本,或者将修改后的JDK类打包到新的jar中,并将其预先添加到引导类路径中
虽然前一种方法是非侵入性的,并且与您在运行时环境中修改JDK的能力无关,但它也是间接的,并不完全符合您的要求。后一种方法是您要求的,但除了非常特殊的情况外,可能不是您想要做的事情

驱动程序应用程序:

package de.scrum\u master.app;
导入java.io.File;
公共类文件漏洞扩展文件{
公共文件漏洞(字符串文件名){
超级(文件名);
}
公共静态void main(字符串[]args){
新文件(“File.txt”);
新文件loophole(“loophole.txt”);
}
}
方面:

package de.scrum\u master.aspect;
导入java.io.File;
公共方面文件拦截器{
对象周围(字符串文件名):调用(文件+.new(字符串))&&args(文件名){
System.out.println(thisJoinPoint+“->”+文件名);
返回继续(文件名);
}
无效(字符串文件名):执行(文件+.new(字符串))&&args(文件名){
System.out.println(thisJoinPoint+“->”+文件名);
继续(文件名);
}
}
控制台输出:

call(java.io.File(String))->File.txt
调用(de.scrum_master.app.FileLoophole(String))->loophole.txt
执行(de.scrum_master.app.FileLoophole(String))->loophole.txt
注意:
call(*.new(..)
返回一个对象,而
execution(*.new(..)
不返回,这就是为什么
around()
通知的返回类型是
void
。这些语义在中进行了描述


更新:您在评论中询问了内部类。好吧,我的切入点适用于静态内部类,没有任何变化。但非静态内部类需要在其构造函数中包含其周围类的实例。检查一下,我为您创建了一个类+调试方面:

package de.scrum\u master.app;
导入java.io.File;
公共类应用程序{
私有类文件漏洞扩展文件{
公共文件漏洞(字符串文件名){
超级(文件名);
}
}
公共静态void main(字符串[]args){
新文件(“File.txt”);
新应用程序().newFileLoophole(“loophole.txt”);
}
}
package de.scrum\u master.aspect;
公共方面文件拦截器{
before():在(de.scrum\u master.app.Application)内{
System.out.println(此连接点);
}
}
现在查看控制台日志:

staticinitialization(de.scrum\u master.app.Application.)
执行(void de.scrum_master.app.Application.main(String[]))
调用(java.io.File(字符串))
调用(de.scrum\u master.app.Application())
预初始化(de.scrum\u master.app.Application())
初始化(de.scrum\u master.app.Application())
执行(de.scrum\u master.app.Application())
调用(类java.lang.Object.getClass())
调用(de.scrum_master.app.Application.FileLoophole(应用程序,字符串))
静态初始化(de.scrum\u master.app.Application.FileLoophole.)
预初始化(de.scrum\u master.app.Application.FileLoophole(应用程序,字符串))
初始化(de.scrum_master.app.Application.FileLoophole(应用程序,字符串))
执行(de.scrum_master.app.Application.FileLoophole(应用程序,字符串))
正如您在日志末尾所看到的,内部类的构造函数被转换为将周围的类实例作为其第一个参数的东西,因此不匹配。现在,知道了这一点,我们可以更改原始切入点以捕获所有构造函数:

void around():执行(文件+.new(..){
System.out.println(此连接点);
继续();
}
如果仍要捕获文件名,则会变得更复杂:

void-around(字符串文件名):执行(文件+.new(*,字符串))&&args(*,文件名){
System.out.println(thisJoinPoint+“->”+文件名);
继续(文件名);
}

感谢您的回复,文件+模式确实非常有用。我又测试了几次,发现了一个拐弯处的箱子。当FileLoophole是一个内部类时,FileInterceptor方面似乎不会拦截它。让我知道我是否应该把这个问题作为一个新问题发布,我仍然在学习礼仪。