【问题标题】:Library load order different on two machines两台机器上的库加载顺序不同
【发布时间】:2013-06-23 21:09:40
【问题描述】:

在我们的 WAR 中,我们有三个 JAR,其中包括 org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager 类。一个 jar 的版本比其他的旧。

这不会在我们的开发或暂存环境中导致问题,但是在我们的生产环境中,加载了来自不同 JAR 的类的一个版本,导致我们在启动时出现 java.lang.NoSuchMethodError。我采用了在生产中失败的完全相同的 WAR,并在阶段和开发中成功运行它。

在 Java 中,什么决定了选择哪个 jar 版本的类?什么会导致一个盒子与另一个盒子不同?

【问题讨论】:

    标签: java classpath war


    【解决方案1】:

    您永远无法知道 Classloader 将按什么顺序加载 jar [您可以通过使用选项 -verbose:class 来可视化它] 或修改它。 因此,既然您知道 2 个 jar 具有相同文件的问题,那么只需放置正确的 jar,否则无论您是否出错,这只是运气。

    编辑:你也可以检查这个问题How to find which jars and in what order are loaded by a classloader?

    【讨论】:

    • 您的回答不清楚,非常令人困惑,抱歉。我不确定你在说什么。不同的加载顺序显然确实具有一定的一致性,否则我之前会遇到这个特定的异常。因为它只发生在一种环境中,所以肯定有不同的东西。
    • 同意它显示一致性,但您不能更改顺序或强制 jvm 遵循特定顺序。这就是我想说的。您可以查看事物的加载方式,但无法控制它。希望澄清。
    • 还想补充一点,java 没有透露任何关于用于加载 jar 的算法。所以它对我们来说是隐藏的,可以根据他们的感觉进行更改。
    • @lok​​i +1 - OP 也没有透露任何关于测试、开发和生产环境之间差异的细节。使用 Java,您永远不应该使用同一个类加载器加载 3 个相同名称的类。必须重新审视战争的包装。
    • 我可以说测试和生产环境完全一样。
    【解决方案2】:

    Loki 是对的,而且结论将是相同的:必须重新审视你的战争包装。您不能让同一个类加载器加载三个相同命名的类,并保证首先加载哪个类。

    每个 servlet 容器,例如 Jetty、Tomcat(顺便说一句,您没有指定使用哪个容器)重新实现某种 webappClassLoader 以保证 - 根据 servlet 规范 2.5+ - 提供的 jar 类war 的加载优先于父类加载器的加载。这是与 Java 提供的 ÙRLClassLoader 的主要区别,后者首先搜索父类加载器。 servlet 规范没有详细说明应该如何进行搜索。

    换句话说,如果您更改 servlet 容器或仅更改 servlet 容器的版本,则您的战争行为可能会再次发生变化。底层文件系统也可能会产生影响(区分大小写/不区分大小写)等...

    ==> 你必须重新打包你的战争。

    假设您使用的是 Tomcat 7,这里有一个指向 findClass method of Tomcat WebappClassLoader 的链接

    【讨论】:

      【解决方案3】:

      Jar 加载顺序由文件系统决定。如果文件系统中的默认顺序更改,则加载顺序会更改。文件系统的顺序由操作系统决定,至少在某些操作系统上,最终的创建顺序决定了加载顺序。请注意,这不是文件的创建日期,而是在文件系统上创建文件的实际时间。因此,您可以拥有外观相同的文件系统,它们在 jar 加载中表现出不同的行为。 最终没有控制顺序的好方法,所以你要确保你的类路径中没有不同版本的相同 jar。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-05-29
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-02-12
        • 2012-11-29
        • 1970-01-01
        相关资源
        最近更新 更多