更改 Java 类加载器的优先级

Changing the Priority of Java Classloaders

可能有人问过这个问题,但如果是这样我就找不到了。

提问

在 Java 1.7/1.8 中是否有一种方法,除了实现自定义类加载器之外,还可以使应用程序类路径比扩展类路径具有更高的优先级(更早加载)?

问题

我们有一个包含多个使用 Apache 的 log4j 库的应用程序的平台。我们还在 [jre]/lib/ext 中安装了自定义 JCA 安全提供程序, 使用 log4j。为此,必须将 log4j 与提供程序一起安装在 ext 目录中。

平台上的一个应用程序(Apache 的 activemq)依赖于 log4j/slf4j 比提供商的旧版本。由于提供商的 log4j jar 在 [jre]/lib/ext 中,它们覆盖了 activemq 的,导致发生 NoSuchMethodError:

java.lang.NoSuchMethodError: org.slf4j.spi.LocationAwareLogger.log(Lorg/slf4j/Marker;Ljava/lang/String;ILjava/lang/String;Ljava/lang/Throwable;)V

那么有没有办法让应用程序类路径中的 jar 取代扩展目录?

复制

您可以通过将这些 jar 安装到 [jre]/lib/ext 来重现此问题:

并通过在应用程序类路径中安装这些 jar:

并通过 运行 此代码:

import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;

import org.slf4j.LoggerFactory;
import org.slf4j.spi.LocationAwareLogger;

public class LogApp {

    public final static LocationAwareLogger logger =
            (LocationAwareLogger) LoggerFactory.getLogger(LogApp.class);

    public static void main(String[] args) throws NoSuchAlgorithmException {
        // Make sure custom security provider has been initialized.
        SecureRandom rand = SecureRandom.getInstanceStrong();
        rand.doubles();

        //ClassLoader cl = ClassLoader.getSystemClassLoader();

        LogApp.logger.error("error: {}: {}", "string", new Exception());
        LogApp.logger.log(null, LogApp.class.getCanonicalName(),
                logger.ERROR_INT, "some message", new Exception());
    }

}

哪个应该产生这个错误:

Exception in thread "main" java.lang.NoSuchMethodError: org.slf4j.spi.LocationAwareLogger.log(Lorg/slf4j/Marker;Ljava/lang/String;ILjava/lang/String;Ljava/lang/Throwable;)V
    at LogApp.main(LogApp.java:23)

回答

运行 Java 使用此参数:

-Xbootclasspath/p:[path_to_app_libs]/log4j-1.2.14.jar:[path_to_app_libs]/slf4j-api-1.5.11.jar:[path_to_app_libs]/slf4j-log4j12-1.5.11.jar

如果您可以控制 java 是 运行 的方式,您可以使用 -Xbootclasspath/a:path/to/your.jar 在扩展之前强制加载特定的 jar。

在 lib/ext 中升级 log4j 可能是更简单的解决方案。希望 lib/ext 中需要 log4j 的任何内容都可以在新版本中使用,否则,无论如何你都会遇到问题,因为 bootclasspath 将优先并强制你的 lib/ext 代码使用新版本。

无论如何,拥有包含特定版本 log4j 的库都是不好的做法。除了最终应用程序之外的任何东西,都应该使用没有特定绑定的 slf4j,或者可能是 JUL。