I recently found contradictory documentation whether service loader will locate providers added to the module path after boot.
The ServiceLoader::reload
:
public void reload()
Clear this loader's provider cache so that all providers will be reloaded. After invoking this method, subsequent invocations of the iterator or stream methods will lazily locate providers (and instantiate in the case of iterator) from scratch, just as is done by a newly-created service loader.
This method is intended for use in situations in which new service providers can be installed into a running Java virtual machine.
Which clearly indicates that the service resolution is completely dynamic.
From the other hand ModuleFinder::findAll
contradicts it.
"A ModuleFinder is used to find modules during resolution or service binding." — Javadoc
Set<ModuleReference> findAll()
Returns the set of all module references that this finder can locate. A ModuleFinder provides a consistent view of the modules that it locates. If findAll is invoked several times then it will return the same (equals) result each time. For each ModuleReference element in the returned set then it is guaranteed that find will locate the ModuleReference if invoked to find that module.
According to this quote the resolution is fixed at module layer creation which is actually expected as the whole Java Platform Module System is static by design. If new providers would've require other modules it would have to modify the existing module graph.
So my question is: Is the first quote left-over docs from Java 8 Javadoc, or there might really be cases where I can add new providers dynamically?
Here I'm going to prove that the module finder docs is correct:
Class com.service.Service
:
package com.service;
import java.util.ServiceLoader;
import java.util.stream.Collectors;
public interface Service {
public static void main(String[] args) throws InterruptedException {
ServiceLoader<Service> loader = ServiceLoader.load(Service.class);
for (int i = 0; i < 5; i++) {
System.out.print("Attempt " + (i + 1) + ": ");
System.out.println(loader
.stream()
.map(ServiceLoader.Provider::type)
.map(Object::toString)
.collect(Collectors.joining(", ")));
Thread.sleep(5000);
loader.reload();
}
}
}
module-info
:
module service {
exports com.service;
uses com.service.Service;
}
In a different module, class com.provider.Provider
:
package com.provider;
import com.service.Service;
public class Provider implements Service {
}
module-info
:
module provider {
exports com.provider;
requires service;
provides com.service.Service with com.provider.Provider;
}
Here's a live GIF what happens when I first run it without the provider in the modulepath. On the second run the provider is already there, I'll try to remove it while runnning.