Proposal: #ServiceLoaderEnhancements

Mark Reinhold mark.reinhold at oracle.com
Mon Sep 12 15:13:01 UTC 2016


Issue summary
-------------
 #ServiceLoaderEnhancements --- The module system encourages the use of
 services for loose coupling, but the `ServiceLoader` class is not very
 flexible. Consider enhancing it so that (1) neither a provider class
 nor its no-args constructor need be declared `public`, (2) a provider
 can be a singleton, or perhaps a collection of singletons, and (3) the
 classes of the available providers can be inspected and selected prior
 to instantiation. [13]
Proposal
--------
 (1) No change: Continue to require service-provider classes, and their
 no-args constructors, to be public.
 Providers on the class path, and their no-args constructors, must
 always be public. Allowing a class-path provider or its no-args
 constructor to be non-public introduces a security risk, since an
 adversary could place a `META-INF/services` entry elsewhere on the
 class path in order to force that otherwise-inaccessible
 constructor to be invoked.
 For providers in named modules, allowing non-public provider
 classes and non-public no-args constructors isn't really necessary
 and is, in some ways, counterproductive. In a named module a
 provider class, and its constructor, can be encapsulated by placing
 the provider in an unexported package. Having to declare the
 provider class and its no-args constructor `public` is a useful
 declaration of intent, since they will be accessed by the service
 loader itself, and hence useful documentation.
 (2) Revise the `ServiceLoader` class so that if a candidate provider
 class in a named module has a no-args public static method named
 `provider` then that method is invoked and its result is taken as
 the provider object. An exception is thrown if the method does
 not have an appropriate return type. A static `provider` method
 can either return a singleton or act as a factory method. If the
 candidate provider class does not have such a method then its
 public no-args constructor, if any, is invoked, per (1) above.
 (An alternative is to use an annotation, say `@Provider`, to
 identify provider-containing fields or provider-returning methods.
 The cost of loading the annotation-reading code into the JVM is,
 however, nontrivial, and since services are used widely within the
 JDK itself we'd prefer not to impose that overhead on all
 applications.)
 (3) Decouple the loading of provider classes from the instantiation of
 such classes: Introduce a new `ServiceLoader.Provider` interface
 that pairs a provider class with a method to instantiate that
 provider, and add a `stream()` method that returns a stream of
 objects implementing that interface. A client can then filter
 providers by inspecting the elements of the stream, examining each
 provider class and perhaps the annotations thereon, and then
 instantiating the class if appropriate. (Draft Javadoc source
 attached below.)
[1] http://openjdk.java.net/projects/jigsaw/spec/issues/#ServiceLoaderEnhancements
--
 /**
 * Represents a service provider located by {@code ServiceLoader}.
 *
 * <p> When using a loader's {@link ServiceLoader#stream() stream()} method
 * then the elements are of type {@code Provider}. This allows processing
 * to select or filter on the provider class without instantiating the
 * provider. </p>
 *
 * @param <S> The service type
 * @since 9
 */
 public static interface Provider<S> extends Supplier<S> {
 /**
 * Returns the provider class. There is no guarantee that this type is
 * accessible and so attempting to instantiate it, by means of its
 * {@link Class#newInstance() newInstance()} method for example, will
 * fail when it is not accessible. The {@link #get() get()} method
 * should instead be used to obtain the provider.
 *
 * @return The provider class
 */
 Class<S> type();
 /**
 * Returns an instance of the provider.
 *
 * @return An instance of the provider.
 *
 * @throws ServiceConfigurationError
 * If the service provider cannot be instantiated. The error
 * cause will carry an appropriate cause.
 */
 @Override S get();
 }
 /**
 * Returns a stream that lazily loads the available providers of this
 * loader's service. The stream elements are of type {@link Provider
 * Provider}, the {@code Provider}'s {@link Provider#get() get} method
 * must be invoked to get or instantiate the provider.
 *
 * <p> When processing the stream then providers that were previously
 * loaded by stream operations are processed first, in load order. It then
 * lazily loads any remaining providers. If a provider class cannot be
 * loaded, can't be assigned to the service type, or some other error is
 * thrown when locating the provider then it is wrapped with a {@code
 * ServiceConfigurationError} and thrown by whatever method caused the
 * provider to be loaded. </p>
 *
 * <p> If this loader's provider caches are cleared by invoking the {@link
 * #reload() reload} method then existing streams for this service
 * loader should be discarded. </p>
 *
 * <p> The following examples demonstrate usage. The first example
 * creates a stream of providers, the second example is the same except
 * that it sorts the providers by provider class name (and so locate all
 * providers).
 * <pre>{@code
 * Stream<CodecSet> providers = ServiceLoader.load(CodecSet.class)
 * .stream()
 * .map(Provider::get);
 *
 * Stream<CodecSet> providers = ServiceLoader.load(CodecSet.class)
 * .stream()
 * .sorted(Comparator.comparing(p -> p.type().getName()))
 * .map(Provider::get);
 * }</pre>
 *
 * @return A stream that lazily loads providers for this loader's service
 *
 * @since 9
 */
 public Stream<Provider<S>> stream() { ... }


More information about the jpms-spec-experts mailing list

AltStyle によって変換されたページ (->オリジナル) /