【问题标题】:How can I verify linking between compiled Java code?如何验证已编译 Java 代码之间的链接?
【发布时间】:2010-07-28 18:40:50
【问题描述】:

一般来说,一组代码(客户端代码)链接到另一组代码(API 代码)。 Java 链接通常在编译时在 .java 和 .class 之间或在运行时在 .class 和 .class 之间进行验证。然而,在后一种情况下,验证是在遇到错误引用时进行的(即它是惰性的)。

有没有办法通过编译的代码一次强制验证客户端代码和 API 代码之间的所有链接?目的是验证客户端代码是否可以与给定版本的 API 一起工作 - 即使它是针对另一个版本编译的。

(当然一种方法是针对 API 进行反编译和重新编译,但有更直接的方法吗?)

【问题讨论】:

    标签: java dynamic-linking


    【解决方案1】:

    由于语言的性质和 JVM 的实现,对链接的强制验证很困难。

    我相信这个问题的基本原理是防止在运行时出现链接错误,这样做的意图非常有效。但是,如果从JVM的角度来看链接错误的原因,那么在不影响性能的情况下强制执行验证或多或少是困难的。

    在运行时,当 JVM 执行与 method invocation instructions 对应的字节码时,会引发链接错误。通常在这一点上延迟的final pass of the JVM's bytecode verifier 被踢了,这可能会导致链接错误。不用说,从性能的角度来看,这是昂贵的。

    (我的假设是)大多数项目(包括商业项目)因此避免强制验证,而是依靠构建和依赖管理系统来避免痛苦。更多评论在this SO question;选择 OSGi 框架的答案可能会有所帮助。

    【讨论】:

    • 性能不一定是主要问题,因为结果可以记住。此外,在某些情况下,代码量不一定很大。
    • 好的,好的。然后,您可能希望使用 ASM(请参阅 CheckClassAdapter 的使用)或 BCEL(其中包含 JustIce 验证程序)之类的字节码工程库来执行验证(如果它们足够),或者在需要时扩展它们。我不确定 Javassist 是否有任何类似的功能。
    【解决方案2】:

    也许您可以针对 ClassPath 中的 jar 指定的 API 运行 junit 测试,然后只需为不同版本的 api 切换 jar 并再次运行测试。您可以轻松地自动执行此操作。

    【讨论】:

      【解决方案3】:

      可以通过反射性分析来自 java 类的代码。请参阅包java.reflect。一些分析工具使用此功能来获取有关已编译代码的信息。最好的例子可能是FindBugs

      这个 API 作为限制,我不认为你可以用它做你想做的事。如果在方法中调用了依赖项,则反射 API 可以找到该方法及其参数,但无法为您提供该方法中使用的依赖函数。

      所以我认为Java中不存在这样的分析工具。

      一种解决方案是一次性提供代码及其所有依赖项。在开发过程中,Maven 等生命周期管理工具可以帮助您管理项目的依赖关系。

      【讨论】:

        【解决方案4】:

        对于您的需要,您可以使用 JarJarDiff[1] 之类的工具并获取新旧版本 API jar 之间的差异。然后将任务转换为验证您没有使用任何不兼容的 API。虽然不是自动的,但这种方法会引起您对更改的注意,而不仅仅是二进制兼容性。

        如果您只想检查二进制兼容性,最简单的方法是针对新 JAR 重新编译项目。更困难(也更脆弱)的是扫描类路径和 调用每个类的每个方法,寻找链接异常。请注意,这种幼稚的方法不会测试所有可能的路径并且几乎没有好处。

        [1]http://depfind.sourceforge.net/tasks/jarjardiff.html

        【讨论】:

          【解决方案5】:

          借鉴this questionAlfresco Wiki的思路,您可以尝试-Xcomp启动一个测试JVM并预编译类以检查是否存在链接错误。

          【讨论】:

            【解决方案6】:

            我建议使用诸如 asm 之类的字节码工具来“访问”代码,并让您的访问者覆盖 visitMethodInsn。然后在所有者类上使用反射来寻找具有指定名称和签名的方法。您还需要查看调用指令的操作码——它将是invokevirtual、invokeinterface、invokespecial 和invokestatic 之一。注意要注意invokevirtual和invokeinterface的区别;对invokevirtual 的调用不会成功调用接口方法,而对invokeinterface 的调用不会成功调用未在接口上定义的方法。

            【讨论】:

              猜你喜欢
              • 2011-06-03
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2015-11-13
              • 1970-01-01
              • 1970-01-01
              • 2010-12-26
              相关资源
              最近更新 更多