What's the purpose of 'uses' directive in Java 9?
Why is it fundamental for the module system to know uses of a particular service ...
Because of dependency resolution. The State of the Module System says in the example a few lines above your quoted text:
In order for the
java.sql
module to make use of this driver, theServiceLoader
class must be able to instantiate the driver class via reflection; for that to happen, the module system must add the driver module to the module graph and resolve its dependencies ...
The key point is that reflection is used to do the instantiation. It happens after module resolution ... and after the application has started running.
... especially how will this introduce efficiency?
Scanning a codebase for all calls to ServiceLoader::load
is expensive. It is not sufficient to just know that a method is called (which could be done by analysis of classfile dependencies). You also need to know what parameters were used to determine what classes are going to be loaded. And (as the SotMS document points out) that would be open to errors; e.g. if the parameters are runtime expressions rather than compile-time constant expressions.
The solution they have adopted is to provide a way to explicitly declare a dependency on a reflectively loaded class.
When the JVM launches, the module system resolves dependencies and builds the module graph. Only modules that make it into the graph are available at run time (even if others are observable). If modules are properly decoupled via services, there is a good chance, though, that the providing modules are not transitive dependencies of the initial module. So without further efforts, service provider modules would routinely not make it into the module graph and thus not be available at run time when a module tries to use a service.
In order for the
java.sql
module to make use of this driver [...] the module system must add the driver module to the module graph and resolve its dependencies [...].
So for services to properly work, provider modules must make it into the module graph even if they are not transitively required from the initial module. But how can the module system identify which modules are needed as service providers? All that use a provides
clause? That would be a little too much. No, only providers of services that are actually needed should be resolved.
This makes it necessary to identify services uses. As others have pointed out, bytecode analysis is slow and unreliable, so a more explicit mechanism is needed to guarantee efficiency and correctness: uses
clauses. Only with them can the module system reliably and efficiently make all service provider modules available.
If the application is a module, then its module declaration must have a
uses
directive that specifies the service; this helps to locate providers and ensure they will execute reliably.
You can observe this behavior if launching a service-based application with the flag --show-module-resolution
:
root monitor
monitor requires monitor.observer
[...]
monitor binds monitor.observer.beta
monitor binds monitor.observer.alpha
The module monitor binds the modules monitor.observer.alpha
and monitor.observer.beta
even though it does not depend on either of them.
(Quotes are from The State of the Module System; emphasis mine.)
Quoting from Java 9 javadoc of ServiceLoader
(emphasis added by me):
An application obtains a service loader for a given service by invoking one of the static
load
methods of ServiceLoader. If the application is a module, then its module declaration must have a uses directive that specifies the service; this helps to locate providers and ensure they will execute reliably. In addition, if the service is not in the application module, then the module declaration must have a requires directive that specifies the module which exports the service.