How does provides...with and 'uses' work in modular Java?

Asked

Viewed 96 times

4

In Java 9, as a result of the Jigsaw project, we gained the ability to define modules. The modules are very simple, but there are still two things that confuse me in them, which is the use of provides...with and uses.

Both in the documentation refer to the use of "services", which I understand are the interfaces and/or abstract classes. A module that uses provides...with is a service provider, and one that uses uses is a service consumer.

But why use them? And how to use them? The exports and requires are not able to do the same job?

  • Java 9 modules came after Osgi. Mainly the concept of service in Osgi, which is very close to what is defined in Java 9. Now regarding your question, the reason for provides and uses exist, is to do the ServiceLoader work right with modules. See https://docs.oracle.com/javase/9/docs/api/java/util/ServiceLoader.html

  • The answer does not answer "why use them?" and "Are Exports and requires not able to do the same job?" Moreover, only my own opinion improves the translation of this.

1 answer

1

Module Exports

A Java module must explicitly export all packages in the module that must be accessible to other modules using the module. Exported packages are declared in module descriptor. Here is how a simple export declaration is displayed within a module descriptor:

module com.jenkov.mymodule {
    exports com.jenkov.mymodule;
}

This example exports the package called com.jenkov.mymodule.

Note that only the listed package is exported. No "subpackage" of the exported package is exported. This means that if the mymodule package contains a subpackage called util, the package com.jenkov.mymodule.util will not be exported only because the com.jenkov.mymodule is.

To export a subpackage as well, you must explicitly state it in module descriptor, thus:

module com.jenkov.mymodule {
    exports com.jenkov.mymodule;
    exports com.jenkov.mymodule.util;
}

You do not need to export the parent package to export a subpackage. The following export declaration from module descriptor is perfectly valid:

module com.jenkov.mymodule {
    exports com.jenkov.mymodule.util;
}

This example exports only the package com.jenkov.mymodule.util and not the package com.jenkov.mymodule.

Module Requires

If a Java module requires another module to do its job, that other module should also be specified in module descriptor. Here is an example of a Java module requires declaration:

module com.jenkov.mymodule {
    requires javafx.graphics;
}

This example of a module descriptor declares that it requires the standard Java module called javafx.graphics.

Services

A service consists of two main parts:

  1. A service interface (service interface).
  2. One or more service implementations (service implementations).

The service interface is usually located in a Java module that contains only the service interface, as well as any service interface related classes and interfaces.

Service implementations are provided by separate Java modules - not by the service interface module. Typically, a Java service implementation module will contain a single service implementation.

A Java module or application may require the module and the service interface code, without knowing exactly what other module delivers the service implementation. Service implementation is discovered at runtime and depends on which service implementation modules are available in the path of the Java module when the application is started.

Module Service Interface

The Java service interface modules do not require a special declaration of the service interface. You have just created a regular Java module. Here is an example of a Java service module descriptor:

module com.jenkov.myservice {
    exports com.jenkov.myservice
}

Note how the actual service interface is not mentioned. The service interface module exports only the Java package that contains the service interface. The service interface is just a normal Java interface, so I haven’t shown an example of this.

Module Service Implementation

A Java module that wants to implement a service interface from a service interface module should:

Imagine that the module com.jenkov.myservice contains an interface called com.jenkov.myservice.MyService. Imagine also that you want to create a service implementation module for this service interface. Imagine that your implementation is called com.blabla.myservice.MyServiceImpl. To declare service implementation, the module descriptor for the service implementation module would look like this:

module com.blabla.myservice {
    requires com.jenkov.myservice;

    provides com.jenkov.myservice.MyService with
       com.blabla.myservice.MyServiceImpl
}

The module descriptor first declares that it requires the service interface module. Second, the module descriptor declares that it provides an implementation for the service interface com.jenkov.myservice.MyService through the class com.blabla.myservice.MyServiceImpl.

Now that this module declares that it implements the service interface, we need to see how a Java module can search for a service interface implementation at runtime.

Service Client Module

Once you have a service interface module and a service implementation module, you can create a client module that uses the service. Sometimes a service client (service client) module is called the Consumer service module (service consumer) or service user module (service user), but the meaning is the same - a module that uses a service specified in an external module and implemented by another external module.

To use the service, the client (client) module must declare in its module descriptor that it uses the service. Here is how to declare the use of a service in a module descriptor:

module com.client.myservicelient {
    requires com.jenkov.myservice;

    uses com.jenkov.myservice.MyService;
}

Note how the module descriptor client (client) also declares that it requires the module com.jenkov.myservice which contains the service interface. It is not necessary to require service implementation modules. These are searched at runtime. Only the service interface module should be required.

The advantage of not having to declare service implementation modules is that implementation modules can be exchanged without breaking customer code. You can decide which service implementation to use when assembling the application - by dropping the desired service implementation modules in the module path. The customer module and the service interface module are therefore dissociated from the service implementation modules.

Now, the service client (service client) module can search for a service interface implementation at runtime like this:

Iterable<MyService> services =
         ServiceLoader.load(MyService.class);

The Iterator returned contains a list of implementations of the MyService. In fact, it will contain all the implementations found in the modules found in the module path. The client (client) module can now iterate the Iterator and find the service implementation you want to use.

Bibliography

http://tutorials.jenkov.com/java/modules.html

Browser other questions tagged

You are not signed in. Login or sign up in order to post.