JAVA面向方面编程 - 运行时方面编织和类加载时间方面编织

时间:2012-12-08 20:04:07

标签: java aop spring-aop

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

在java中,我可以想象,而不是理解编译时编程方面实际上会发生的方式。代理类在类编译过程中生成(在项目中启用了方面)。生成的字节码将具有代理代码。

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

3 个答案:

答案 0 :(得分:6)

Spring AOP 实际上将Java Dynamic Proxies用于接口,如果需要,还使用 cglib 作为非接口类型。它只适用于Spring Beans。为所有与所谓的切入点匹配的方法自动生成代理。这是在布线期间完成的。

然而,

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

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

答案 1 :(得分:2)

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

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

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

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

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

答案 2 :(得分:1)

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

要查看其工作原理非常简单,例如使用AspectJ。您可以使用编译时编织和反汇编代码来确切了解它是如何工作的。还可以指示AspectJ在加载时编织期间将生成的类保存在磁盘上。