为什么Java类加载在Linux上失败,但在Windows上成功?

时间:2009-02-06 10:36:25

标签: java cross-platform classloader

我有一个使用Jetty部署的Java Web应用程序(使用Spring)。如果我尝试在Windows机器上运行它,一切都按预期工作,但如果我尝试在我的Linux机器上运行相同的代码,它会像这样失败:

[normal startup output]
11:16:39.657 INFO   [main] org.mortbay.jetty.servlet.ServletHandler$Context.log>(ServletHandler.java:1145) >16> Set web app root system property: 'webapp.root' = [/path/to/working/dir]
java.lang.reflect.InvocationTargetException
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
        at java.lang.reflect.Method.invoke(Method.java:597)
        at org.mortbay.start.Main.invokeMain(Main.java:151)
        at org.mortbay.start.Main.start(Main.java:476)
        at org.mortbay.start.Main.main(Main.java:94)
Caused by: java.lang.ExceptionInInitializerError
        at org.springframework.web.util.Log4jWebConfigurer.initLogging(Log4jWebConfigurer.java:129)
        at org.springframework.web.util.Log4jConfigListener.contextInitialized(Log4jConfigListener.java:51)
        at org.mortbay.jetty.servlet.WebApplicationContext.doStart(WebApplicationContext.java:495)
        at org.mortbay.util.Container.start(Container.java:72)
        at org.mortbay.http.HttpServer.doStart(HttpServer.java:708)
        at org.mortbay.util.Container.start(Container.java:72)
        at org.mortbay.jetty.Server.main(Server.java:460)
        ... 7 more
Caused by: org.apache.commons.logging.LogConfigurationException: org.apache.commons.logging.LogConfigurationException: No suitable Log constructor [Ljava.lang.Class;@15311bd for org.apache.commons.logging.impl.Log4JLogger (Caused by java.lang.NoClassDefFoundError: org/apache/log4j/Category) (Caused by org.apache.commons.logging.LogConfigurationException: No suitable Log constructor [Ljava.lang.Class;@15311bd for org.apache.commons.logging.impl.Log4JLogger (Caused by java.lang.NoClassDefFoundError: org/apache/log4j/Category))
        at org.apache.commons.logging.impl.LogFactoryImpl.newInstance(LogFactoryImpl.java:543)
        at org.apache.commons.logging.impl.LogFactoryImpl.getInstance(LogFactoryImpl.java:235)
        at org.apache.commons.logging.impl.LogFactoryImpl.getInstance(LogFactoryImpl.java:209)
        at org.apache.commons.logging.LogFactory.getLog(LogFactory.java:351)
        at org.springframework.util.SystemPropertyUtils.(SystemPropertyUtils.java:42)
        ... 14 more
Caused by: org.apache.commons.logging.LogConfigurationException: No suitable Log constructor [Ljava.lang.Class;@15311bd for org.apache.commons.logging.impl.Log4JLogger (Caused by java.lang.NoClassDefFoundError: org/apache/log4j/Category)
        at org.apache.commons.logging.impl.LogFactoryImpl.getLogConstructor(LogFactoryImpl.java:413)
        at org.apache.commons.logging.impl.LogFactoryImpl.newInstance(LogFactoryImpl.java:529)
        ... 18 more
Caused by: java.lang.NoClassDefFoundError: org/apache/log4j/Category
        at java.lang.Class.getDeclaredConstructors0(Native Method)
        at java.lang.Class.privateGetDeclaredConstructors(Class.java:2389)
        at java.lang.Class.getConstructor0(Class.java:2699)
        at java.lang.Class.getConstructor(Class.java:1657)
        at org.apache.commons.logging.impl.LogFactoryImpl.getLogConstructor(LogFactoryImpl.java:410)
        ... 19 more
Caused by: java.lang.ClassNotFoundException: org.apache.log4j.Category
        at java.net.URLClassLoader$1.run(URLClassLoader.java:200)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.net.URLClassLoader.findClass(URLClassLoader.java:188)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:307)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:252)
        at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:320)
        ... 24 more
[shutdown output]

我用java -verbose:class运行应用程序,并根据该输出,org.apache.log4j.Category从我的/ WEB-INF / lib中的log4j JAR加载,就在第一个异常出现之前抛出。

现在,两台机器上的Java版本略有不同。这两台机器都有Sun的java,Linux机器有1.6.0_10,而Windows机器有1.6.0_08,或者可能是07或06,我现在不记得确切的数字,而且手头没有机器。但即使Javas的次要版本略有不同,代码也不应该像这样破坏。有谁知道这里有什么问题?

6 个答案:

答案 0 :(得分:4)

你必须明白一个类加载器看不到一切;他们只能看到父类加载器已加载或自己加载了什么。因此,如果你有两个类加载器,比如一个用于Jetty,另一个用于你的webapp,你的webapp可以看到log4j(因为JAR是WEB-INF / lib),但是Jetty的类加载器不能。

如果您设法让一个类可用于Jetty(例如DB层中的某些东西),它使用log4j但最终在Jetty的上下文(和类加载器)中运行,则会出错。

要调试此操作,请在org.springframework.web.util.Log4jWebConfigurer.initLogging()中设置断点。如果可以,请将此类的源代码复制到您的项目中(之后不要忘记将其删除)并添加以下行:

ClassLoader cl = Thread.currentThread().getContextClassLoader();

查看调试器中的cl对象。这应该会给你一些创建它的信息。我的猜测是这是Jetty的类加载器。

[编辑]请注意,如果在两个类加载器中都有log4j,则会遇到不同的混乱:在这种情况下,您将拥有两个类,其中相同的名称创建不兼容赋值的对象!因此,请确保此jar只有一个实例,或者在两个上下文之间永远不会传递log4j的实例(这通常是不可能的)。

答案 1 :(得分:1)

这似乎是一个经典的类加载器问题。这可能是由于首先加载了另一个Web应用程序,它也使用了log4j,但版本与您正在测试的应用程序所使用的版本不同。类加载器使用它找到的类的第一个版本。通常可以在配置文件中更改服务器类加载策略。对不起,我对此有点生疏,但也许它可以指出你正确的方向。

  1. 确保Web服务器上没有其他已安装的应用
  2. 确保正在加载的log4j是正确的版本
  3. 确保您没有潜伏在服务器类路径中某处的log4j。
  4. HTH

答案 2 :(得分:0)

你在两台机器上使用相同的WAR吗?您是否检查过WAR文件是否相同(没有发生传输错误)?

答案 3 :(得分:0)

需要考虑的一些随机事项:

(1)检查在web-app目录之外的linux实例上是否还有其他版本的log4j?

(2)是否正在使用apache commons日志?您可能想要考虑使用SLF4J吗?

(3)JAR / WAR在某种程度上是否已损坏 - 它是用ASCII还是二进制FTP?

(4)在每种情况下打印出类加载器层次结构,只是为了查看是否存在差异?

答案 4 :(得分:0)

即使问题解决了原来的问题,我也会指出在Windows与Linux(或Unix)上运行相同代码时常见的问题来源是区分大小写的问题。 Windows忽略大小写,而Linux或Unix区分大小写。这不止一次地困扰我。

因此,如果您在类路径上指定了jar或目录,但它不是正确的情况,那么它将在Linux上失败但在Windows上成功。这也可以是FileNotFoundExceptions的源。

答案 5 :(得分:0)

遇到同样的问题,找到了一个简单的解决方案/解决方法:

在Eclipse中的首选项> Java>安装JRE,选择JRE>编辑和添加外部JAR ...并浏览到log4j.jar。

另一种解决方法是将log4j.jar添加到Classpath选项卡中的每个启动定义。