This most likely refers to the bind operation of resolveAndBind
processing uses
—provides
relationships.
This method works exactly as specified by resolve
except that the graph of resolved modules is augmented with modules induced by the service-use dependence relation.
More specifically, the root modules are resolved as if by calling resolve
. The resolved modules, and all modules in the parent configurations, with service dependences
are then examined. All modules found by the given module finders that provide
an implementation of one or more of the service types are added to the module graph and then resolved as if by calling the resolve
method. Adding modules to the module graph may introduce new service-use dependences and so the process works iteratively until no more modules are added.
So, binding takes place after resolving, but still before any code of the involved modules has been invoked. Namely, it doesn’t matter whether the ServiceLoader
is actually used within the module(s) or not. But when it is used, it will utilize the already available information. So a lot of potential problems have been precluded at this point already. This is also the reason why we could build optimized module images pre-linking such services.
This, however, doesn’t work with automatic modules, as they don’t have uses
directives. Without that information, service lookups can only be done when an actual use of ServiceLoader
happens, just like with classes loaded through the old classpath.
Note that the issue in the linked Q&A is a bit different. According to the OP’s information, a module declaration has been used when compiling. But then, the OP ran the application using the -jar
option which puts the specified jar file on the classpath and loads it from there, creating an unnamed module, rather than an automatic module. This ignores the compiled module-info
and in absence of old-fashioned META-INF/services/…
resources, no service implementation was found at all. The OP’s code was designed to fall back to the default service implementation if none had been found, which is indistinguishable from the scenario of finding the default service through the ServiceLoader
.
The crucial differences of your answer are the start method -m base.service/base.service.ServiceUser
, which will locate the base.service
through the module path and launch its base.service.ServiceUser
, as well as --add-modules …user.service
, to ensure that the module providing the service will be added to the runtime environment. The latter is needed since there is no direct dependency from the launched module base.service
to the user.service
, so it wouldn’t be loaded automatically.
So in your answer’s setup, both modules are loaded as such. When they have a module-info
, its provides
directive will be processed to find the service implementation, otherwise, it’s an automatic module and must have a META-INF/services/…
file. As said above, there is no equivalent to the uses
directive for automatic modules, so you will never see a “binds” log entry for them. But they may still use the service lookup.
unnamed
module resolution via the code and command in OP's question. Follow-up question, in the last command of my answer ..java --show-module-resolution -p base-service/target/base-service-1.0-SNAPSHOT.jar:user-service/target/user-service-1.0-SNAPSHOT.jar --add-modules base.service,user.service -m base.service/base.service.ServiceUser user1
, without theMETA-INF/services/…
, the user service is loaded and not the default one, why would that be the case? – Keverne