Restrictions on service loading
mark.reinhold at oracle.com
mark.reinhold at oracle.com
Mon Dec 12 23:24:35 UTC 2016
2016/12/9 6:59:57 -0800, david.lloyd at redhat.com:
> It doesn't seem clear whether this is a spec or implementation issue, so
> I'm posting here to start.
> The gist of the problem is, code running in a module cannot use
> ServiceLoader to find things that it hasn't been declared to "use" even
> if a target class loader is specified. This doesn't really make a lot
> of sense because in the case where you're specifying a class loader, you
> are pretty clearly establishing that you want to search the class
> loader, not a module space.
That's true prior to SE 9, though even if you specify a class loader
when trying to locate a service you could get a provider from some
other loader that's related to that loader via parent delegation.
Once modules are part of the picture then if you specify a class loader
when trying to locate a service you could, additionally, get a provider
from some other loader in the same non-boot layer as that loader, or in
the same non-boot layer as one of that loader's parents. The motivation
for this is to make the "right thing" happen in module-per-loader
scenarios, i.e., in layers where every module has its own class loader.
These details are laid out in the "Locating providers" section of the
`ServiceLoader` API specification .
> This can be worked around by adding a "uses" at run time, but it
> shouldn't be necessary for the explicit class loader case.
Service-use declarations have both benefits and, naturally, costs.
The benefit of static `uses` declarations is that they make it easier to
configure complex systems. The resolver will locate providers on the
module path for you, without any further instruction, and resolve them
recursively as needed. If you forget to include a `uses` declaration for
a service that you actually use, and you invoke a `ServiceLoader.load`
method to load it, then you get an immediate error that tells you that
you forgot the declaration, so that you can add it. This is preferable
to failing silently because no providers were discovered because you
forgot the `uses` declaration. It's also preferable to not supporting
`uses` declarations in the module sysem at all, in which case the only
way to configure providers would be to request them explicitly via
run-time options such as `--add-modules`, in the case of the boot layer,
or by specifying them explicitly when creating other layers via the API.
A cost of static `uses` declarations is that, sometimes, sophisticated
users of services must do a little bit of work to get past the above
check. If, in particular, one module uses `ServiceLoader` to locate
providers on behalf of a second module then the first module will have to
invoke the `Module::addUses` method, passing in the service type obtained
from the second, in order to enable the run-time `uses` check to succeed.
Having to invoke `addUses` can be an annoyance, but we expect very few
developers to have to do it. Many developers, and in particular many
non-expert developers, will benefit from the sanity checking that static
`uses` declarations enable, hence the cost is justified.
More information about the jpms-spec-observers