Use java serviceloader build with jdk8 use in >= java9

Peter :

I have two jars. One provides a service interfaces and a service loading class and one provides the implementation of that service.

This works perfectly when running this in jdk8, but I get an service type not accessible to unnamed module @3754a4bf error when running on jdk9 or higher.

I migrated that two jars to a module base jar and that works great when running on >= jdk9, but that fails on jdk8 of cause, because of wrong class file version.

So, I don't have problem using java serivceloader api with java 8 or 9.

I'm aware of https://blog.codefx.org/tools/multi-release-jars-multiple-java-versions/ , but I want to avoid to make the build process more complicated. The build already involves class relocation and other stuff.

My question: Is there a way to use the same jars running on jdk8 and >=jdk9 using java serviceloading api?

rzwitserloot :

In JDK9 and up, all jars are effectively 'modules'. If they do not have a module definition (which you create by making a file named module-info.java and putting a module declaration inside), its name is 'unnamed module @whatever', it exports all its packages, and can access anything exported by any other module (in module-speak: it 'reads' everything). Meaning, if all your classpath dependencies are such unnamed modules, they all export everything and they all read everything thus they all can access anything marked public from anybody else – which is how it worked in JDK8, and thus, why it's all compatible.

To be clear: in JDK9, for code in module (jar) A to access a method, class, or field from module (jar) B, then in addition to the usual access modifiers (the public keyword), B needs to 'export' that package, and A needs to read that module, or it doesn't work. If you aren't explicitly writing module info files for either side, well, then you get the default behaviour which is 'export everything' and 'read everything', bringing us back to the JDK8 scenario of: the thing has to be marked public, and then you can access it.

I actually do not recommend you make that module-info file; that's a big step, one you should only take once you're familiar with the module system.

The error means that the 'service type'[1] is not 'accessible'[2] to 'unnamed module @3754a4bf'[3]. Let's break that into pieces:

[1] serviceloader works by defining an interface that a 'service provider' implements, and the class using the service loader then ends up with a bunch of instances of that interface; each representing one implementation. It's the Foo in ServiceLoader.load(Foo.class).

[2] 'accessible' is module speak for: Either the code that needs this did not explicitly 'read' it, or the code that has this did not export it.

[3] 'unnamed module@3754abf' is the name of the module that is trying to access it.

Taking this all together, it means: You're operating under false assumptions: Whatever jar contains your service interface (that Foo I was talking about), is NOT an unnamed module, or, possibly, isn't public. Note that if it is a public interface inside a not-public class (i.e.: /* package private */ class Example { public interface MyServiceInterface {} }), that probably still counts as 'not public enough'.

If it's a named module (which means: it has a module-info.java file), then export the package that the service interface is in. See any jigsaw (the name of the java module system) tutorial on how to set that up. If it's not, ensure it's a public interface or abstract class, and if its inside some other type, make sure those are public too. If neither is the case, check your classpaths; javac is rather adamant that one of these two is the case.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=366729&siteId=1