Services – Java Module System

19.9 Services

Programming to interfaces is a powerful design paradigm that advocates writing code using interfaces and abstract classes—that is, using abstract types and not concrete types. This allows new implementations of abstract types to be used by the code if and when necessary. This strategy results in the code being loosely coupled.

The requires directives in module declarations create explicit dependencies between concrete modules, requiring explicit naming of modules with heavy reliance on concrete types, thus making the modules tightly coupled. As we shall see, services allow programming to interfaces, but at a higher level of abstraction, resulting in the modules being loosely coupled, as direct dependencies between them are removed.

The main advantage of services is application extensibility: adding functionality by providing new services without having to recompile the whole application. This is feasible as services are discoverable at runtime. This design strategy is typically employed in implementing plugins which can extend the capabilities of an application.

A service is a specific abstract type, typically an interface, but it can be an abstract class as well, that specifies some specific functionality. The following artifacts are typically required to create a service:

  • The service interface

The service interface specifies the abstract type that represents the service. It is also known as a service provider interface.

  • The service provider

A service provider implements the service. There can be zero or more service providers of a service. A service provider advertises the implementation of a specific service with the provides-with directive.

  • The service locator

The service locator discovers service providers at runtime through a service loader which is an instance of the java.util.ServiceLoader<S> class, where type parameter S designates the type of service to be loaded. A service loader locates and loads service providers at runtime. A service locator announces the services it is interested in with the uses directive.

  • The service consumer

A service consumer utilizes the service. The service consumer accesses a particular implementation of the service via the service locator, and does not communicate directly with a service provider. It can choose which implementation of the service to consume from the ones provided by the service locator.

We illustrate the setup necessary to use services by implementing a variant of the adviceApp we have seen earlier (Example 19.1, Figure 19.12). Each artifact required to set up a service will be implemented as a module in our example below, although other configurations are possible, typically where the service locator is merged with either the service interface or the service consumer. The service providers, being autonomous modules, can be added or removed as necessary.

Figure 19.15, p. 1198 shows the module diagram and the module declarations of the adviceService application, and Example 19.3 to Example 19.6 show the source code in each module of the application.

Figure 19.15 Module Graph and Module Declarations for Services

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *