在 Java 8 中加载自定义 TimeZoneNameProvider [英] Loading a custom TimeZoneNameProvider in Java 8

查看:28
本文介绍了在 Java 8 中加载自定义 TimeZoneNameProvider的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试为 Java 8 中的时区创建自定义命名.

所以我创建了一个 java.util.spi.TimeZoneNameProvider 的子类并实现了所需的方法.按照 文档 我也有创建了一个名为 ./src/main/resources/META-INF/services/java.util.spi.TimeZoneNameProvider 的文件,其中包含一行带有我的实现的完全限定名称.jar 文件中的最终文件与我在文档中找到的内容相匹配.

我遇到的问题是当我运行我的应用程序或任何 junit 测试时它没有被加载.

注意:我还有一个已加载的自定义 java.time.zone.ZoneRulesProvider.所以 SPI 正在工作,只是不适用于 TimeZoneNameProvider.

经过一番挖掘,我发现 TimeZoneNameProvider 是 LocaleServiceProvider 的子类,SPI 加载由 sun.util.locale.provider.SPILocaleProviderAdapter 完成.

据我所知,SPILocaleProviderAdapter 通过调用 java.util.ServiceLoader.loadInstalled(Class<S> service) 加载服务,记录为

<块引用>

此方法适用于仅安装的提供程序想要的.生成的服务将只查找和加载提供者已经安装到当前的Java虚拟机中;提供者应用程序的类路径将被忽略.

因此此代码路径明确不会加载任何自定义内容.

作为一个实验,我创建了一些代码来强制使用替代实现,而不是默认的 SPILocaleProviderAdapter,它本质上只使用不同的类加载器......这使我的自定义命名规则处于活动状态.

我的问题:如何正确加载自定义 TimeZoneNameProvider 实现?

或者这是我应该报告的 Java 8 中的错误吗?

<小时>

更新:附加信息.

我已经用 Java 8、11 和 13 测试了我的代码,但在所有情况下都失败了.

但是!如果我执行 mvn clean package -Djava.locale.providers=JRE,SPI 那么在 Java 8 中它仍然不起作用,而在 Java 11 和 13 中它突然起作用了.

Java 11 &13 效果显然符合预期,因为 Java 13 文档明确指出默认情况下禁用 SPI.

<块引用>

需要实现区域设置敏感的应用程序服务必须显式指定SPI"以便 Java 运行时从类路径加载它们.

解决方案

在 Java 81 中,TimeZoneNameProvider,这是一个 LocaleServiceProvider,是 Java 扩展机制的一部分.正如 javadoc 所说:

<块引用>

这些区域设置敏感服务的实现使用 Java 扩展机制作为已安装的扩展.

正如 Java™ 教程小径 所述:

<块引用>

已安装的扩展

已安装的扩展是 Java 运行时环境 (JRE™) 软件的 lib/ext 目录中的 JAR 文件.

<块引用>

下载扩展

下载扩展是 JAR 文件中的类集(和相关资源).JAR 文件的清单可以包含引用一个或多个下载扩展的标头.可以通过以下两种方式之一引用扩展:

  • 通过 Class-Path 标头
  • 通过 Extension-List 标头

由于 javadoc 说已安装的扩展",我不知道下载扩展"是否已安装.功能适用于区域设置服务提供商,但值得一试,因此请阅读跟踪以了解其工作原理,然后尝试一下.

否则,当您安装其余代码时,您必须在 JRE 的 lib/ext 目录中安装带有自定义 TimeZoneNameProvider 的 jar.

1) Java 9 中取消了 Java 扩展机制,因此它在 Java 9+ 中的工作方式有所不同.

I'm trying to create custom naming for a timezone in Java 8.

So I created a subclass of java.util.spi.TimeZoneNameProvider and implemented the required methods. Following the documentation I have also created a file called ./src/main/resources/META-INF/services/java.util.spi.TimeZoneNameProvider which contains a single line with the fully qualified name of my implementation. The final file in the jar file matches what I find in the documentation.

The problem I have is that it does not get loaded when I run my application or any of the junit tests.

NOTE: I also have a custom java.time.zone.ZoneRulesProvider which IS loaded. So SPI is working, just not for the TimeZoneNameProvider.

After some digging around I found that TimeZoneNameProvider is a subclass of LocaleServiceProvider and the SPI loading is done by sun.util.locale.provider.SPILocaleProviderAdapter.

As far as I can tell the SPILocaleProviderAdapter loads the service by calling java.util.ServiceLoader.loadInstalled(Class<S> service) which is documented as

This method is intended for use when only installed providers are desired. The resulting service will only find and load providers that have been installed into the current Java virtual machine; providers on the application's class path will be ignored.

So this code path explicitly does not load anything custom.

As an experiment I've created some code to force an alternative implementation to be used instead of the default SPILocaleProviderAdapter that essentially only uses a different classloader... which makes my custom naming rules active.

My question: How do I correctly load a custom TimeZoneNameProvider implementation?

Or is this a bug in Java 8 that I should report?


Update: Additional info.

I have tested my code with Java 8, 11 and 13 and in all cases it fails.

However! If I do mvn clean package -Djava.locale.providers=JRE,SPI then in Java 8 it still does not work and in Java 11 and 13 it suddenly works.

The Java 11 & 13 effect is apparently as intended because the Java 13 documentation explicitly states that SPI is disabled by default.

Applications which require implementations of the locale sensitive services must explicitly specify "SPI" in order for the Java runtime to load them from the classpath.

解决方案

In Java 81, a TimeZoneNameProvider, which is a LocaleServiceProvider, is a part of the Java Extension Mechanism. As the javadoc says:

Implementations of these locale sensitive services are packaged using the Java Extension Mechanism as installed extensions.

As the Java™ Tutorials Trail says:

Installed Extensions

Installed extensions are JAR files in the lib/ext directory of the Java Runtime Environment (JRE™) software.

Download Extensions

Download extensions are sets of classes (and related resources) in JAR files. A JAR file's manifest can contain headers that refer to one or more download extensions. The extensions can be referenced in one of two ways:

  • by a Class-Path header
  • by an Extension-List header

Since the javadoc says "installed extensions", I don't know if the "download extensions" feature works for Locale Service Providers, but it's worth a try, so read the trail to see how it works, and try it out.

Otherwise you must install the jar with your custom TimeZoneNameProvider in the lib/ext directory of the JRE when you install the rest of your code.

1) The Java Extension Mechanism was eliminated in Java 9, so it'll work differently in Java 9+.

这篇关于在 Java 8 中加载自定义 TimeZoneNameProvider的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆