科技行者

行者学院 转型私董会 科技行者专题报道 网红大战科技行者

知识库

知识库 安全导航

至顶网软件频道基础软件JRockit JVM对AOP的支持

JRockit JVM对AOP的支持

  • 扫一扫
    分享文章到微信

  • 扫一扫
    关注官方公众号
    至顶头条

  面向方面编程(Aspect-Oriented Programming,AOP)正在软件社区和企业界中获得强大的发展动力。自从20世纪90年代Xerox引入了AOP之后,AOP经过研究团体、开源社区和企业界的数次推动和革新

作者:中国IT实验室 来源:中国IT实验室 2007年8月24日

关键字:

  • 评论
  • 分享微博
  • 分享邮件
 关于这些代理的先后次序有一个问题;在联结点(或切点)级别上没有控制次序的细粒度配置方法。

  某些其他情况可能导致更加无法预测的结果。例如,当一个字段访问被截取时,这往往意味着字段获取(field get)字节码指令被移动到一个新添加的方法,并且被替换为对这个新方法的调用。因此,下一个编织器将在代码中的另一个位置(在那个新添加的方法中)看到一个字段访问,而它自己的匹配机制和配置可能不匹配这个位置。

  总之,主要问题如下:

  • 代理看到哪些字节码?问题是,正常情况下,被编织的字节码是从类装载管道获得的,但是建立类数据库所依赖的字节码是从硬盘读取的。当涉及多个代理时,硬盘上的字节码不再是正在执行的字节码了;因为某个代理可能已经修改了字节码,这意味着第二个代理看到的是错误的字节码视图。当使用HotSwap API时,也会发生这种情况。
  • 当代理A撤消或者改变它的编织操作时,可能会出现问题。如果另一个代理B在代理A之后已经执行了修改,那么代理B可能已经重新构造了字节码,导致字节码看起来完全不一样了(尽管其功能是一样的),在此情况下,代理A就不知道该怎么做了。

截取反射式调用是不可能的

  当前的编织方式只能测试(至少是部分地)可静态确定的执行流。请考虑以下代码示例,它在给定的实例foo上调用方法void doA()。

public void invokeA(Object foo) throws Throwable {
  Method mA = foo.getClass().getDeclaredMethod("doA",
new Class[0]); mA.invoke(foo, new Object[0]); }

  在现代的代码库中常常使用这种反射式访问来创建实例、调用方法或者访问字段。

  从字节码的角度来看,对方法void doA()的调用是看不到的。编织器只看到对java.lang.reflect API的调用。还没有简单且高效的办法可以对通过反射执行的调用进行编织。目前,这对于如何执行编织以及如何实现AOP是很重要的限制。最好的办法是,开发人员使用执行端切点来代替。显然,从JVM的角度来看,存在一个对doA()方法的方法调度,尽管这在源代码或字节码中没有出现。已经证明,JVM编织是以高效的方式解决这个问题的唯一编织机制。

其他问题

  某些人对字节码测试持怀疑态度,尤其是在动态执行的情况下(在装载时或运行时)。对于动态修改代码,存在着一种不应低估的情绪化影响,尤其是在与某种盲目的革命性新技术(比如AOP或服务的透明式插入)结合使用时。在涉及多个代理时可能发生的混乱将增加人们的怀疑。

  另一个潜在的问题是Java规范中对类文件规定的64Kb边界。方法体的字节码指令总长度被限制为64Kb。在编织已经很大的类文件(例如,将JSP文件编译为servlet时产生的类文件)时,这可能会导致问题。在处理这个类时,可能会突破64Kb的限制,这就会导致运行时错误。

提议的解决方案

  对于上面讨论的大多数问题,JVM编织是自然的解决方案。为了理解其原因,我们将查看两个示例。这些示例说明,JVM已经做了执行编织所需的大多数工作:当类被装载时,JVM读取字节码以便建立为java.lang.reflect.* API服务所需的数据。另一个例子是方法调度。现代的JVM将方法或代码块的字节码编译为更高级而且更高效的构造和执行流(在可以应用代码内联的地方进行代码内联)。由于HotSwap API的需要,JRockit JVM(可能还包括其他JVM)还会记录哪个方法调用了其他方法,这样如果在运行时重新定义某个类,那么在所有期望的位置(内联的或非内联的),类中定义的方法体仍然可以被热交换。

  因此,不必为了编织进一个建议调用而修改字节码,比如说在特定的方法调用之前。JVM实际上可以掌握关于这个建议调用的知识,它会在任何匹配的联结点上对此建议进行调度,然后再调度实际的方法。

  由于不接触字节码,可以预期到直接的好处,比如:

  • 不会由于字节码测试而导致启动开销。
  • 对于在任何位置、任何时间、以线性开销添加和删除建议的完全的运行时支持。
  • 对建议的反射式调用的隐式支持。
  • 不需要占用额外的内存来将类模型复制到某些框架特有的结构。

  本系列的第二篇文章将详细描述提议的JRockit JVM对AOP的支持。

  以下代码示例作了总结性说明。它在调用sayHello()方法之前对静态方法advice()进行调度:

public class Hello {

  // -- the sample method to intercept
  public void sayHello() {
    System.out.println("Hello World");
  }

  // -- using the JRockit JVM support for AOP
  static void weave() throws Throwable {
    // match on method name
    StringFilter methodName = new StringFilter(
        "sayHello",
        StringFilter.Type.EXACT
    );

    // match on callee type
    ClassFilter klass = new ClassFilter(
        Hello.class,
        false,
        null
    );

    // advice is a regular method dispatch
    Method advice = Aspect.class.getDeclaredMethod(
        "advice",
        new Class[0]
    );

    // get a JRockit weaver and subscribe the
    // advice to the join point picked out by the filter
    Weaver w = WeaverFactory.createWeaver();

    w.addSubscription(new MethodSubscription(
        new MethodFilter(
            0,
            null,
            klass,
            methodName,
            null,
            null
        ),
        MethodSubscription.InsertionType.BEFORE,
        advice
    ));
  }

  // -- sample code

  static void test() {
    new Hello().sayHello();
  }

  public static void main(String a[])
  throws Throwable {
    weave();
    test();
  }

  // -- the sample aspect

  public static class Aspect {

    public static void advice() {
        System.out.println("About to say:");
    }
  }
}
    • 评论
    • 分享微博
    • 分享邮件
    邮件订阅

    如果您非常迫切的想了解IT领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。

    重磅专题
    往期文章
    最新文章