JPMS 是否支持来自 META-INF/services 的自动模块服务?
Does JPMS support services from META-INF/services for automatic modules?
您可以找到以下有关自动模块的信息:
The module system also scans META-INF/services and makes the automatic
module provide the services named therein. An automatic module is
assumed allowed to use all services.
但是,我有以下情况。我想在 JPMS 中使用 log4j2 和 slf4j。为此 log4j-slf4j-impl-2.11.1.jar
必须向 slf4j-api-1.8.0-beta2.jar
提供 JPMS 服务。 log4j的开发者将log4j-slf4j-impl-2.11.1.jar
作为自动模块,通过META-INF/services提供服务。但是,它不起作用,它给出以下内容:
Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/logging/log4j/Logger
at org.apache.logging.log4j.slf4j@2.11.1/org.apache.logging.slf4j.SLF4JServiceProvider.initialize(SLF4JServiceProvider.java:53)
at org.slf4j/org.slf4j.LoggerFactory.bind(LoggerFactory.java:153)
at org.slf4j/org.slf4j.LoggerFactory.performInitialization(LoggerFactory.java:141)
at org.slf4j/org.slf4j.LoggerFactory.getProvider(LoggerFactory.java:419)
at org.slf4j/org.slf4j.LoggerFactory.getILoggerFactory(LoggerFactory.java:405)
at org.slf4j/org.slf4j.LoggerFactory.getLogger(LoggerFactory.java:354)
at org.slf4j/org.slf4j.LoggerFactory.getLogger(LoggerFactory.java:380)
at Log4j2Slf4jJdk11/com.temp.NewMain.<clinit>(NewMain.java:12)
Caused by: java.lang.ClassNotFoundException: org.apache.logging.log4j.Logger
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:582)
at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:521)
... 8 more
我决定将模块信息添加到 log4j-slf4j-impl-2.11.1.jar
并通过 provides ... with..
以 JPMS 方式导出服务。问题解决了 - 我没有得到任何 NoClassDefFoundError
。这是 link 的问题。
所以我的问题是:
- JPMS 是否支持 META-INF/services 自动模块中的服务?
- 如果是,那么如何解释这种行为?
编辑
共有 5 个模块:
slf4j-api-1.8.0-beta2.jar // name: org.slf4j
log4j-slf4j18-impl-2.11.1.jar // name: org.apache.logging.log4j.slf4j
log4j-core-2.11.1.jar // name: org.apache.logging.log4j.core
log4j-api-2.11.1.jar // name: org.apache.logging.log4j
log4j2-slf4j-jdk11-1.0-SNAPSHOT.jar // name: Log4j2Slf4jJdk11
VARIANT 1 如果我 运行 --show-module-resolution when log4j-slf4j18-impl-2.11.1.jar
has META-INF/services
我得到以下输出(我用 ...
替换了完整路径):
...
root Log4j2Slf4jJdk11 file:.../log4j2-slf4j-jdk11-1.0-SNAPSHOT.jar
Log4j2Slf4jJdk11 requires org.slf4j file:.../slf4j-api-1.8.0-beta2.jar
jdk.compiler binds org.apache.logging.log4j.core file:.../log4j-core-2.11.1.jar automatic
org.slf4j binds org.apache.logging.log4j.slf4j file:.../log4j-slf4j18-impl-2.11.1.jar automatic
VARIANT 2 如果我 运行 --show-module-resolution when log4j-slf4j18-impl-2.11.1.jar
has module-info
我得到以下输出:
...
root Log4j2Slf4jJdk11 file:.../log4j2-slf4j-jdk11-1.0-SNAPSHOT.jar
Log4j2Slf4jJdk11 requires org.slf4j file:.../slf4j-api-1.8.0-beta2.jar
jdk.compiler binds org.apache.logging.log4j.core file:.../log4j-core-2.11.1.jar automatic
org.slf4j binds org.apache.logging.log4j.slf4j file:.../log4j-slf4j18-impl-2.11.1.jar
org.apache.logging.log4j.slf4j requires org.slf4j file:.../slf4j-api-1.8.0-beta2.jar
org.apache.logging.log4j.slf4j requires org.apache.logging.log4j.core file:.../log4j-core-2.11.1.jar automatic
org.apache.logging.log4j.slf4j requires org.apache.logging.log4j file:.../log4j-api-2.11.1.jar
org.apache.logging.log4j binds org.apache.logging.log4j.core file:.../log4j-core-2.11.1.jar automatic
在 VARIANT 1 中,来自 org.apache.logging.log4j.slf4j
的服务无法从 org.apache.logging.log4j.core
加载 class (org.apache.logging.log4j.Logger
)。在 VARIANT 2 中,来自 org.apache.logging.log4j.slf4j
的服务从 org.apache.logging.log4j.core
加载所有 classes,一切正常。我们在 VARIANT 2 的输出中看到有一行
org.apache.logging.log4j.slf4j requires org.apache.logging.log4j.core
而VARIANT 1中没有这一行。问题出在这吗?但是如果两个模块是自动的,是不是就不能自动解析了呢?
SLF4J 1.8 需要 org.slf4j.spi.SLF4JServiceProvider 的实现作为公开服务。它在 log4j-slf4j18-impl jar 中发现。但是,Log4j SLF4J 桥需要 Log4J API(模块 org.apache.logging.log4j)。尽管这是一个显式的 Java 模块,但因为它只是从一个自动模块中引用,所以它没有被加载,从而导致 ClassNotFoundException。
对此的简单解决方案是在启动应用程序时在命令行中包含 --addmodules=org.apache.logging.log4j。
The module system also scans META-INF/services and makes the automatic module provide the services named therein. An automatic module is assumed allowed to use all services.
但是,我有以下情况。我想在 JPMS 中使用 log4j2 和 slf4j。为此 log4j-slf4j-impl-2.11.1.jar
必须向 slf4j-api-1.8.0-beta2.jar
提供 JPMS 服务。 log4j的开发者将log4j-slf4j-impl-2.11.1.jar
作为自动模块,通过META-INF/services提供服务。但是,它不起作用,它给出以下内容:
Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/logging/log4j/Logger
at org.apache.logging.log4j.slf4j@2.11.1/org.apache.logging.slf4j.SLF4JServiceProvider.initialize(SLF4JServiceProvider.java:53)
at org.slf4j/org.slf4j.LoggerFactory.bind(LoggerFactory.java:153)
at org.slf4j/org.slf4j.LoggerFactory.performInitialization(LoggerFactory.java:141)
at org.slf4j/org.slf4j.LoggerFactory.getProvider(LoggerFactory.java:419)
at org.slf4j/org.slf4j.LoggerFactory.getILoggerFactory(LoggerFactory.java:405)
at org.slf4j/org.slf4j.LoggerFactory.getLogger(LoggerFactory.java:354)
at org.slf4j/org.slf4j.LoggerFactory.getLogger(LoggerFactory.java:380)
at Log4j2Slf4jJdk11/com.temp.NewMain.<clinit>(NewMain.java:12)
Caused by: java.lang.ClassNotFoundException: org.apache.logging.log4j.Logger
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:582)
at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:521)
... 8 more
我决定将模块信息添加到 log4j-slf4j-impl-2.11.1.jar
并通过 provides ... with..
以 JPMS 方式导出服务。问题解决了 - 我没有得到任何 NoClassDefFoundError
。这是 link 的问题。
所以我的问题是:
- JPMS 是否支持 META-INF/services 自动模块中的服务?
- 如果是,那么如何解释这种行为?
编辑 共有 5 个模块:
slf4j-api-1.8.0-beta2.jar // name: org.slf4j
log4j-slf4j18-impl-2.11.1.jar // name: org.apache.logging.log4j.slf4j
log4j-core-2.11.1.jar // name: org.apache.logging.log4j.core
log4j-api-2.11.1.jar // name: org.apache.logging.log4j
log4j2-slf4j-jdk11-1.0-SNAPSHOT.jar // name: Log4j2Slf4jJdk11
VARIANT 1 如果我 运行 --show-module-resolution when log4j-slf4j18-impl-2.11.1.jar
has META-INF/services
我得到以下输出(我用 ...
替换了完整路径):
...
root Log4j2Slf4jJdk11 file:.../log4j2-slf4j-jdk11-1.0-SNAPSHOT.jar
Log4j2Slf4jJdk11 requires org.slf4j file:.../slf4j-api-1.8.0-beta2.jar
jdk.compiler binds org.apache.logging.log4j.core file:.../log4j-core-2.11.1.jar automatic
org.slf4j binds org.apache.logging.log4j.slf4j file:.../log4j-slf4j18-impl-2.11.1.jar automatic
VARIANT 2 如果我 运行 --show-module-resolution when log4j-slf4j18-impl-2.11.1.jar
has module-info
我得到以下输出:
...
root Log4j2Slf4jJdk11 file:.../log4j2-slf4j-jdk11-1.0-SNAPSHOT.jar
Log4j2Slf4jJdk11 requires org.slf4j file:.../slf4j-api-1.8.0-beta2.jar
jdk.compiler binds org.apache.logging.log4j.core file:.../log4j-core-2.11.1.jar automatic
org.slf4j binds org.apache.logging.log4j.slf4j file:.../log4j-slf4j18-impl-2.11.1.jar
org.apache.logging.log4j.slf4j requires org.slf4j file:.../slf4j-api-1.8.0-beta2.jar
org.apache.logging.log4j.slf4j requires org.apache.logging.log4j.core file:.../log4j-core-2.11.1.jar automatic
org.apache.logging.log4j.slf4j requires org.apache.logging.log4j file:.../log4j-api-2.11.1.jar
org.apache.logging.log4j binds org.apache.logging.log4j.core file:.../log4j-core-2.11.1.jar automatic
在 VARIANT 1 中,来自 org.apache.logging.log4j.slf4j
的服务无法从 org.apache.logging.log4j.core
加载 class (org.apache.logging.log4j.Logger
)。在 VARIANT 2 中,来自 org.apache.logging.log4j.slf4j
的服务从 org.apache.logging.log4j.core
加载所有 classes,一切正常。我们在 VARIANT 2 的输出中看到有一行
org.apache.logging.log4j.slf4j requires org.apache.logging.log4j.core
而VARIANT 1中没有这一行。问题出在这吗?但是如果两个模块是自动的,是不是就不能自动解析了呢?
SLF4J 1.8 需要 org.slf4j.spi.SLF4JServiceProvider 的实现作为公开服务。它在 log4j-slf4j18-impl jar 中发现。但是,Log4j SLF4J 桥需要 Log4J API(模块 org.apache.logging.log4j)。尽管这是一个显式的 Java 模块,但因为它只是从一个自动模块中引用,所以它没有被加载,从而导致 ClassNotFoundException。
对此的简单解决方案是在启动应用程序时在命令行中包含 --addmodules=org.apache.logging.log4j。