2

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

【正常启动输出】
11:16:39.657 INFO [main] org.mortbay.jetty.servlet.ServletHandler$Context.log>(ServletHandler.java:1145) >16> 设置 Web 应用根系统属性:'webapp.root' = [/path/到/工作/目录]
java.lang.reflect.InvocationTargetException
        在 sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        在 sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
        在 sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
        在 java.lang.reflect.Method.invoke(Method.java:597)
        在 org.mortbay.start.Main.invokeMain(Main.java:151)
        在 org.mortbay.start.Main.start(Main.java:476)
        在 org.mortbay.start.Main.main(Main.java:94)
引起:java.lang.ExceptionInInitializerError
        在 org.springframework.web.util.Log4jWebConfigurer.initLogging(Log4jWebConfigurer.java:129)
        在 org.springframework.web.util.Log4jConfigListener.contextInitialized(Log4jConfigListener.java:51)
        在 org.mortbay.jetty.servlet.WebApplicationContext.doStart(WebApplicationContext.java:495)
        在 org.mortbay.util.Container.start(Container.java:72)
        在 org.mortbay.http.HttpServer.doStart(HttpServer.java:708)
        在 org.mortbay.util.Container.start(Container.java:72)
        在 org.mortbay.jetty.Server.main(Server.java:460)
        ... 7 更多
引起:org.apache.commons.logging.LogConfigurationException:org.apache.commons.logging.LogConfigurationException:没有合适的日志构造函数[Ljava.lang.Class;@15311bd for org.apache.commons.logging.impl.Log4JLogger(引起by java.lang.NoClassDefFoundError: org/apache/log4j/Category) (由 org.apache.commons.logging.LogConfigurationException 引起:没有合适的日志构造函数 [Ljava.lang.Class;@15311bd 用于 org.apache.commons.logging。 impl.Log4JLogger(由 java.lang.NoClassDefFoundError: org/apache/log4j/Category 引起))
        在 org.apache.commons.logging.impl.LogFactoryImpl.newInstance(LogFactoryImpl.java:543)
        在 org.apache.commons.logging.impl.LogFactoryImpl.getInstance(LogFactoryImpl.java:235)
        在 org.apache.commons.logging.impl.LogFactoryImpl.getInstance(LogFactoryImpl.java:209)
        在 org.apache.commons.logging.LogFactory.getLog(LogFactory.java:351)
        在 org.springframework.util.SystemPropertyUtils.(SystemPropertyUtils.java:42)
        ... 14 更多
引起:org.apache.commons.logging.LogConfigurationException:没有合适的日志构造函数 [Ljava.lang.Class;@15311bd for org.apache.commons.logging.impl.Log4JLogger(由 java.lang.NoClassDefFoundError 引起:org/apache /log4j/类别)
        在 org.apache.commons.logging.impl.LogFactoryImpl.getLogConstructor(LogFactoryImpl.java:413)
        在 org.apache.commons.logging.impl.LogFactoryImpl.newInstance(LogFactoryImpl.java:529)
        ... 18 更多
引起:java.lang.NoClassDefFoundError: org/apache/log4j/Category
        在 java.lang.Class.getDeclaredConstructors0(本机方法)
        在 java.lang.Class.privateGetDeclaredConstructors(Class.java:2389)
        在 java.lang.Class.getConstructor0(Class.java:2699)
        在 java.lang.Class.getConstructor(Class.java:1657)
        在 org.apache.commons.logging.impl.LogFactoryImpl.getLogConstructor(LogFactoryImpl.java:410)
        ... 19 更多
引起:java.lang.ClassNotFoundException:org.apache.log4j.Category
        在 java.net.URLClassLoader$1.run(URLClassLoader.java:200)
        在 java.security.AccessController.doPrivileged(本机方法)
        在 java.net.URLClassLoader.findClass(URLClassLoader.java:188)
        在 java.lang.ClassLoader.loadClass(ClassLoader.java:307)
        在 java.lang.ClassLoader.loadClass(ClassLoader.java:252)
        在 java.lang.ClassLoader.loadClassInternal(ClassLoader.java:320)
        ... 24 更多
[关机输出]

我已经使用 运行了该应用程序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,我现在不记得确切的数字,并且手头没有机器. 但即使 Java 的次要版本略有不同,代码也不应该像这样中断。有谁明白这里有什么问题?

4

6 回答 6

5

您必须了解类加载器无法看到所有内容;他们只能看到父类加载器已加载的内容或他们自己加载的内容。因此,如果您有两个类加载器,一个用于 Jetty,另一个用于您的 webapp,您的 webapp 可以看到 log4j(因为 JAR 是 WEB-INF/lib)但 Jetty 的类加载器不能。

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

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

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

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

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

于 2009-02-06T11:05:24.493 回答
1

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

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

高温高压

于 2009-02-06T11:00:16.203 回答
0

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

于 2009-02-06T10:50:54.473 回答
0

一些随机的事情要考虑:

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

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

(3) JAR/WAR 是否以某种方式损坏 - 它是以 ASCII 格式还是二进制格式进行 FTP 处理的?

(4) 打印出每种情况下的类加载器层次结构,看看有没有出入?

于 2009-02-06T10:57:39.733 回答
0

尽管最初的问题已经为提问者解决了,但我会指出,在 Windows 与 Linux(或 Unix)上运行相同代码时,常见的问题来源是区分大小写的问题。Windows 忽略大小写,而 Linux 或 Unix 区分大小写。这不止一次地咬了我。

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

于 2009-04-24T07:06:05.357 回答
0

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

在 Eclipse 中的 Preferences > Java > Installed JREs 中,选择 JRE > Edit and Add External JARs... 并浏览到您的 log4j.jar。

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

于 2011-11-28T17:01:58.110 回答