2015-07-10 27 views
1

我试图使用AspectJ将调用挂接到Java API。例如,假设我对java.io.File有一个方面:针对超级()调用Java API的切入点

import java.io.File; 

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

这会挂钩调用File(String)构造函数。但是它不会对下面的代码做任何事情:

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

https://eclipse.org/aspectj/doc/next/progguide/language-joinPoints.html,我应该使用执行()切入点来处理超()调用来代替。但是,这不起作用,因为执行点位于Java API中,我无法将代码编入。是否有切入点来捕获这些super()调用?有没有办法在事先不知道FileLoophole类的情况下做到这一点?

回答

1

你基本上有两种选择:

  • 使用模式File+为了匹配切入点包括子类。没有必要知道他们的名字。
  • 使用AspectJ二进制(后编译)编织并将您的代码直接从rt.jar注入到JDK类中,创建它的修改版本,或者只是将修改的JDK类打包到新的JAR中,并将其预先加入到引导类路径。

虽然前一种方法是非侵入式的,并且与您在运行时环境中修改JDK的能力无关,但它也是间接的,并不完全符合您的要求。后一种方法是你要求的,但可能不是你想做的事情,除非是非常特殊的情况。

驱动应用:

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"); 
    } 
} 

看点:

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); 
    } 
} 

控制台输出:

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 

P.S。:P租赁注意,虽然call(*.new(..))返回一个对象,execution(*.new(..))不,这就是为什么around()建议的返回类型是void。这些语义在AspectJ documentation中描述。


更新:您询问您的评论内部类。那么,我的切入点适用于静态内部类没有任何改变。但是一个非静态的内部类需要在它的构造函数中使用它的周围类的一个实例。检查了这一点,我为你创建一个类+调试方面:

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); 
    } 
} 

现在看控制台日志:

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)) 

正如你可以在日志的末尾看到的,内部类的构造函数转换为将周围类实例作为其第一个参数的东西,从而导致不匹配。现在,知道,我们可以改变我们原来的切入点,以捕获所有构造函数:

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

如果你仍然想捕捉的文件名,就有点复杂:

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

谢谢对于响应,File +模式确实非常有用。我正在测试这个更多,并发现了一个角落的情况。当FileLoophole是一个内部类时,FileInterceptor方面似乎不会拦截它。让我知道如果我应该将此作为一个新问题发布,我仍然在学习礼仪。 – seccess