为什么Java扩展机制不检查实现LocaleServiceProvider的可选包的类路径? [英] Why does the Java Extension Mechanism not check the classpath for an optional package implementing LocaleServiceProvider?
问题描述
我创建了一个maven项目L
,并编写了 Java扩展(即可选包)实现(即扩展)实现(即扩展)状态
扩展框架利用了类加载委托 机制.当运行时环境需要为 一个应用程序,它将在以下位置查找该类: 顺序:
[...]
- 类路径:类,包括JAR文件中的类, 在系统属性java.class.path指定的路径上.如果是JAR 类路径上的文件具有带有Class-Path属性的清单, 由Class-Path属性指定的JAR文件也将被搜索. 默认情况下,java.class.path属性的值为.,当前 目录.您可以使用-classpath或-cp来更改值 命令行选项,或设置CLASSPATH环境变量. 命令行选项将覆盖CLASSPATH的设置 环境变量.
我相信,Maven会将所有依赖项放在类路径上.
但是,当我在P
中运行单元测试(在IntelliJ中; L
在类路径上)时,它失败:
@Test
public void xyLocalePresent() {
Locale xy = new Locale("xy");
assertEquals("P not on classpath", xy, com.example.l.Locales.XY); // access constant in my Locale project L; should be equals to locale defined here
SimpleDateFormat df = (SimpleDateFormat) DateFormat.getDateInstance(DateFormat.SHORT, xy);
assertEquals("dd/MM/yy", df.toPattern()); // fails; L specifies the short date pattern as dd/MM/yy
}
我必须从-Djava.locale.providers=SPI,JRE -Djava.ext.dirs=/path/to/project/L/target
开始.如果我这样做,它会起作用,表明L
的服务提供程序已成功加载(表明jar
的结构正常).
NB:Java 8 technotes 说默认的订单SPI,JRE
.
为什么,当我将L
放在类路径上时,为什么它不起作用?为什么我必须明确指出它?
更新:在再次阅读JavaDoc之后,我刚刚看到了这一点(强调我的意思):
这些对语言环境敏感的服务的实现打包为 Java扩展机制作为已安装扩展.
这说明了事情. :(
有什么方法可以通过在运行P
时将L
放在类路径上来实现此目的,即无需安装L
(或必须使用-D
系统属性)? (P
使用maven,Struts2和Spring,如果有帮助的话...)
在诸如Web服务器(例如Tomcat)之类的更复杂的应用程序中,有多个ClassLoader,因此可以将由Web服务器提供服务的每个WebApp保持独立. /p>
扩展机制用于扩展核心Java功能,即正在运行的JVM(Web服务器)中全局可用的功能.因此,它们必须由System ClassLoader加载.
向代码Java运行时添加扩展的标准方法是
- 将Jar文件添加到
JRE_HOME/lib/ext
文件夹 - 通过指定
java.ext.dirs
系统属性添加其他要搜索的文件夹
您也可以自己将其添加到Bootstrap ClassPath中,但是如果激活了安全管理器,则可能会导致问题.不确定那部分.因此,最好以正式方式进行操作.
请注意,由CLASSPATH
环境变量或-cp
命令行选项定义的类路径未定义Bootstrap ClassPath.
要了解更多信息,请阅读Java文档"如何分类找到".
I have created a maven project L
and written a Java extension (i.e. an optional package) implementing (i.e. extending) the (abstract) service providers that implement (i.e. extend) LocaleServiceProvider, to support a dialect (let's call it xy
) that isn't normally supported by the JRE. (I do not want to use the CLDR extension that came with Java 8, even though I'm running 8.141.)
The project compiles, and produces a jar
with a META-INF/services
folder that contains the provider-configuration files in UTF-8 with the qualified provider class names being on a line that ends with a line feed (\n
).
I have then declared a maven dependency in my project P
on the locale project L
, and I thought that that would work, because the tutorial states
The extension framework makes use of the class-loading delegation mechanism. When the runtime environment needs to load a new class for an application, it looks for the class in the following locations, in order:
[...]
- The class path: classes, including classes in JAR files, on paths specified by the system property java.class.path. If a JAR file on the class path has a manifest with the Class-Path attribute, JAR files specified by the Class-Path attribute will be searched also. By default, the java.class.path property's value is ., the current directory. You can change the value by using the -classpath or -cp command-line options, or setting the CLASSPATH environment variable. The command-line options override the setting of the CLASSPATH environment variable.
Maven puts all dependencies on the classpath, I believe.
Yet when I run my unit test in P
(in IntelliJ; L
is on the classpath), it fails:
@Test
public void xyLocalePresent() {
Locale xy = new Locale("xy");
assertEquals("P not on classpath", xy, com.example.l.Locales.XY); // access constant in my Locale project L; should be equals to locale defined here
SimpleDateFormat df = (SimpleDateFormat) DateFormat.getDateInstance(DateFormat.SHORT, xy);
assertEquals("dd/MM/yy", df.toPattern()); // fails; L specifies the short date pattern as dd/MM/yy
}
I have to start it with -Djava.locale.providers=SPI,JRE -Djava.ext.dirs=/path/to/project/L/target
. If I do that, it works, indicating that L
's service providers were loaded successfully (indicating the jar
's structure is ok).
NB: the Java 8 technotes say that the order SPI,JRE
is the default.
Why, oh why does it not work when I just put L
on the classpath? Why do I have to point to it explicitly?
Update: After going through the JavaDoc again, I just saw this (emphasis mine):
Implementations of these locale sensitive services are packaged using the Java Extension Mechanism as installed extensions.
That explains things. :(
Is there any way to make this work by just putting L
on the classpath when P
runs, i.e. without having to install L
(or having to use -D
system properties)? (P
uses maven, Struts2 and Spring, if that helps...)
In more complex applications, such as web servers (e.g. Tomcat), there are multiple ClassLoaders, so each WebApp served by the web server can be kept independent.
The extension mechanism is for extending the core Java functionality, i.e. features available globally within the running JVM (the web server). As such, they must be loaded by the System ClassLoader.
The standard way to add an extension to the code Java runtime, is to either
- add the Jar file to the
JRE_HOME/lib/ext
folder - add extra folders to be searched by specifying the
java.ext.dirs
system property
You could also just add it to the Bootstrap ClassPath yourself, but that might cause problems if the Security Manager is activated. Not sure about that part. So it's best to do it the official way.
Note that the classpath defined by the CLASSPATH
environment variable, or the -cp
command-line option, does not define the Bootstrap ClassPath.
To learn more, read the Java documentation "How Classes are Found".
这篇关于为什么Java扩展机制不检查实现LocaleServiceProvider的可选包的类路径?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!