问题
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.
回答1:
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/extdirectory 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-Pathheader- by an
Extension-Listheader
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+.
来源:https://stackoverflow.com/questions/58937553/loading-a-custom-timezonenameprovider-in-java-8