首页 \ 问答 \ 更改Java类加载器的优先级(Changing the Priority of Java Classloaders)

更改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中来重现此问题:

  • 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

It's possible this has already been asked, but if so I've been unable to find it.

The Ask

Is there a way in Java 1.7/1.8, short of implementing a custom classloader, to make the application classpath higher-priority (loaded earlier) than the extension classpath?

The Issue

We have a platform with multiple apps using Apache's log4j library. We also have a custom JCA security provider installed in [jre]/lib/ext which also uses log4j. In order to do so, log4j has to be installed along with the provider in the ext directory.

One of the apps on the platform (Apache's activemq) relies on an older version of log4j/slf4j than the provider's. And since the provider's log4j jars are in [jre]/lib/ext, they override activemq's, causing a NoSuchMethodError to occur:

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

So is there a way to make the jars in the application classpath supersede the extension directory?

Reproduction

You can reproduce this issue by installing these jars into [jre]/lib/ext:

  • log4j-1.2.17.jar
  • slf4j-api-1.7.2.jar
  • slf4j-log4j12-1.7.2.jar

And by installing these jars in the application classpath:

  • log4j-1.2.14.jar
  • slf4j-api-1.5.11.jar
  • slf4j-log4j12-1.5.11.jar

And by running this code:

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());
    }

}

Which should generate this error:

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)

Answer

Run Java with this parameter:

-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

原文:https://stackoverflow.com/questions/37188353
更新时间:2023-06-21 22:06

最满意答案

对于延迟发布问题的解决方案感到抱歉。

我们终于找到了一个解决方 数据作为'bytea'类型存储在数据库中

也许这是同样的情况:

Postgresql,JDBC和流式BLOB

所以答案是这样的:

最初的BIRT报告查询就像

SELECT t.title, t.clientServiceOrderId, t.employeeId, t.tripId, t.clientName, t.address, t.city, t.state, t.status, t.signatureImg, t.ratings ...

我们改成了

SELECT t.title, t.clientServiceOrderId, t.employeeId, t.tripId, t.clientName, t.address, t.city, t.state, t.status, encode(encode(t.signatureImg, 'escape')::bytea, 'base64') signatureImg, t.ratings, ...

这使得列"signatureImg"在结果中显示为"String"数据类型。

然后对于图像组件,使用onCreate方法而不是使用先前的"expression value"

<image id="1746"> <property name="height">83px</property> <property name="width">113px</property> <method name="onCreate"><![CDATA[importPackage(Packages.javax.imageio);importPackage(Packages.java.io);importPackage(Packages.sun.misc);decoder = new BASE64Decoder();decodedBytes = decoder.decodeBuffer(this.getRowData().getColumnValue("signatureimg") == null ? "" : this.getRowData().getColumnValue("signatureimg").toString());this.data = decodedBytes;]]></method> <property name="source">expr</property> </image>


sorry for the delay in posting solution to the problem.

We finally found one solution. The data was being stored in database as a 'bytea' type

maybe this was the same case:

Postgresql, JDBC, and streaming BLOBs

so the answer was like:

The original BIRT report query was like

SELECT t.title, t.clientServiceOrderId, t.employeeId, t.tripId, t.clientName, t.address, t.city, t.state, t.status, t.signatureImg, t.ratings ...

We changed it to

SELECT t.title, t.clientServiceOrderId, t.employeeId, t.tripId, t.clientName, t.address, t.city, t.state, t.status, encode(encode(t.signatureImg, 'escape')::bytea, 'base64') signatureImg, t.ratings, ...

This made the column "signatureImg" appear as "String" datatype in the result.

Then for the image component, instead of using previous "expression value" a onCreate method was used

<image id="1746"> <property name="height">83px</property> <property name="width">113px</property> <method name="onCreate"><![CDATA[importPackage(Packages.javax.imageio);importPackage(Packages.java.io);importPackage(Packages.sun.misc);decoder = new BASE64Decoder();decodedBytes = decoder.decodeBuffer(this.getRowData().getColumnValue("signatureimg") == null ? "" : this.getRowData().getColumnValue("signatureimg").toString());this.data = decodedBytes;]]></method> <property name="source">expr</property> </image>

相关问答

更多