我正在实现一个审计api来记录任何在声明中有@Auditable注释的方法调用。基本要求是它必须由当前的应用程序使用,并且应该是非侵入性的。我已经通过Log4J2包装器获得了审计API,而且我还成功地在一些EJB拦截器中使用了它。然而,我需要创建一个全地形拦截器,无论在哪里使用注释,我都可以使用它,也就是说,我想用它来注释servlet、EJB和POJO方法,让拦截器发挥它的魔力。
我尝试过JavaEEInterceptors,它们只在EJB中工作,我尝试过GUICE,但它既不适用于servlet,也不适用于EJB方法,只适用于GUICE注入的POJO。
我想知道,如果有人知道如何使用,我应该使用什么,如果能举个例子,我将不胜感激。
非常感谢。
一种可能的解决方案是使用AspectJ。AspectJ可以让您定义要拦截的内容(所谓的连接点),并在发生时执行所需的代码(所谓的建议)。
连接点可以是一切,但在您的情况下,您需要使用给定的注释捕获方法的执行,因此我们将只使用"执行"连接点。
然后,AspectJ需要为您的类提供工具。这可以做到:
- 使用AspectJ编译器编译代码,它使用所需的功能扩展了java编译器
- "编织"现有的jar文件,这样你就可以在正常的构建过程之后进行,或者你甚至可以在没有源的jar文件上进行(在这种情况下不是这样,因为你无论如何都需要放置注释)
我们将只使用第一种方法来编译方面,而使用第二种方法来插入您想要的jar文件或类文件夹。
要做到这一点,您需要编写一个注释Auditable,它必须具有运行时保留。
然后你写一个方面:
public aspect Audit { // An aspect then compiles as if it was a class (at runtime, it is a normal class)
Object around() : // This is an around advice
execution(@Auditable * *(..)) // Here you're catching execution of any method of any class with any parameter and any return type, which is annotated with @Auditable
{
// Do what you want to do before the method
long start = System.nanoTime();
Logger.log("I'm entering into " + thisJoinPoint); // with thisJoinPoint you can extract a lot of informations, like what class, what method, what parameters, on which instance etc..
try {
Object ret = proceed(); // "proceed" goes on executing the method
// You could log, or do whatever you want with the return value, which could be "Void".
return ret;
} finally {
Logger.log("It took ns: " + (System.nanoTime() - start));
}
}
}
一旦编写了这个方面(通常是在一个包中,扩展名为".aj",但不是强制性的),就可以使用AspectJ编译器进行编译。
ajc -outxml -classpath thisLib.jar:thatLib.jar -1.5 -outjar auditableAspect.jar package/of/your/AuditableAspect.aj
这类似于使用javac编译java代码。在类路径上,您需要编译方面所需的内容,在本例中,我使用了一个错误的Logger,它必须位于thisLis.jar或thatLib.jar中。
最后,您将获得一个auditableAspect.jar文件,该文件将包含方面的编译类。
现在,要使用它,您需要"编织"希望它"拦截"的类(或jar文件)。要做到这一点,仍然使用ajc命令:
ajc -inpath myStuff.jar -outjar myStuffAudited.jar -aspectPath auditableAspect.jar
然后,您将在myStuffAudited.jar中找到myStuff.jar中的相同类,以及其他一些合成内部匿名类。
现在,基本上您可以从应用程序类路径(如果是WEB应用程序,则为WEB-INF/lib)中删除myStuff.jar,将其替换为myStuffAudited.jar,还可以添加作为方面的auditableAspect.jar和作为AspectJ运行时的aspectjrt.jar。
重新加载您的应用程序,它将开始日志记录。
你可以在所有你想要的罐子上,或者在for循环中的所有罐子上这样做;那些未受影响的人将不受影响。
这可以在构建周期中以多种方式进行集成。
- 显然,.bat或.sh脚本可以实现自动化
- Ant有aspectJ的任务
- Maven有一个aspectJ插件
- Eclipse有一个用于在ide内部编译和编织的插件
不知道你是如何构建你的项目的,所以YMMV。
不管这是否具有侵入性,这都是一种观点;但据我所知,如果不使用仅限于接口的运行时代理(如您所见),那么没有很多包可以在不以某种方式操纵类字节码的情况下提供运行时审计。
还有一个使用编织类加载器的选项,这将完全消除编织现有jar和类的需要,因为它们将在加载时由类加载器本身编织。它工作得很好,我喜欢它,但这取决于您的运行时环境。