更改Java类加载器的优先级

时间:2016-05-12 13:26:48

标签: java log4j classloader slf4j jca

可能已经有人问这个问题,但如果是这样的话,我一直无法找到它。

问询

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:

来重现此问题
  • 的log4j-1.2.17.jar
  • SLF4J-API-1.7.2.jar
  • SLF4J-log4j12-1.7.2.jar

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

  • 的log4j-1.2.14.jar
  • SLF4J-API-1.5.11.jar
  • SLF4J-log4j12-1.5.11.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

1 个答案:

答案 0 :(得分:2)

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

升级lib / ext中的log4j可能更容易解决。希望在lib / ext中需要log4j的任何内容都能在新版本中存在,否则,无论如何都会出现问题,因为bootclasspath优先,并强制你的lib / ext代码使用新版本。

在任何情况下,拥有特定版本的log4j的库是不好的实践。除最终应用之外的任何东西都应该使用没有特定绑定的slf4j,或者可能使用JUL。