3

我遇到了一篇关于 AOP 的文章,其中提到 Aspect weaving 可以在编译时、类加载时和运行时发生。

在 java 中,我可以想象,相当理解,方面编织在编译期间实际上是如何发生的。代理类是在类编译期间生成的(在项目上启用方面)。生成的字节码将具有代理代码。

但我仍然想知道在类加载时间编织和运行时编织期间究竟(实际上)发生了什么。加载类时是否生成代理类?方面库是否在 .class(编译时)文件中添加任何编程指令以生成代理类?

4

3 回答 3

6

Spring AOP实际上对接口使用 Java 动态代理,如果需要,对非接口类型使用cglib 。它仅适用于 Spring Bean。代理会自动为通过所谓的切入点匹配的所有方法生成。这是在接线期间完成的。

然而, AspectJ不需要甚至不使用代理,它直接生成字节码,该字节码被编织到现有的字节码中。AspectJ 功能更强大,可以做的不仅仅是方法拦截。

  • 对于编译时编织 (CTW),这是由 AspectJ 编译器ajc完成的。除了原生 AspectJ 语法(它是 Java 的超集),您还可以使用 Java 注释风格的方式来定义方面,通常称为 @AspectJ 语法。在这种情况下,您可以使用javac编译方面并在单独的构建步骤中使用方面编织器。结果基本相同。在这两种情况下,在运行时,您都需要一个小的 AspectJ 运行时库,以使方面按预期工作。
  • CTW 和 LTW(加载时编织)之间的区别在于编织步骤被推迟到类加载时间。为了完成这项工作,您需要一个在 JVM 命令行上称为 AspectJ weaver 的 Java 代理库。Java 代理在加载普通应用程序类之前启动,因此可以影响类加载并根据需要检测加载的字节码。分析工具或类似工具也使用此方法。
  • 所以很明显,LTW 不适用于源代码,而是类文件,即 AspectJ 可以将其方面代码编织到任何常规 Java 类文件中。这也可以在运行时之前完成,即您可以将方面代码编织到您没有源代码的外部库中,创建一个新的修改版本,以便在每次加载库时为 LTW 节省时间. 这通常称为二元编织。有了一些额外的知识,甚至可以将方面代码编织到 JDK 中,即通过创建一个包含方面代码的修改rt.jar 。但这不是你通常会做的,我只是想提一下这是可能的。
于 2014-05-13T10:32:23.617 回答
2

不一定在编译时。在 Java 中,通过反射和类加载器,您可以在运行时以编程方式查看甚至修改创建方法和类。

例如, "hello".getClass().getMethod("substring", Integer.TYPE).invoke("my sharona", 3) 将返回“sharona”,从 String 实例中提取方法子字符串并将其应用于另一个对象。

使用自定义类加载器,您可以定义如何从系统加载您的类。这样,您可以定义一个方法,该方法将调用以加载(或生成!)类的字节码。您还可以使用系统类加载器来加载类的字节码并检查它。

这种技术被 Mozilla Rhino 广泛使用,它可以使用它将编译的 JavaScript 作为 Java 字节码加载以提高效率,或者JavaAssist,它允许您在运行时创建类、方法、字段和任何东西。

JBoss 或 TomCat 等应用服务器也使用反射来检查和操作代码,尤其是通过注释。

于 2012-12-08T20:28:50.220 回答
1

不确定什么是运行时编织。在加载时编织中,代理拦截类加载并在将字节码传递给类加载器之前对其进行修改。

看看它是如何工作的很容易,例如使用AspectJ。您可以使用编译时编织和反汇编代码来查看它是如何工作的。还可以指示 AspectJ 在加载时编织期间将生成的类保存在磁盘上。

于 2012-12-08T20:38:39.153 回答