Java 9 Modules - Creating Services and Service Providers

[Updated: Oct 7, 2017, Created: Oct 6, 2017]

In Java 9, we can develop Services and Service Providers as modules. A service module declares that it uses one or more interfaces whose implementations will be provided at run time by some provider modules. A provider module declares what implementations of service interfaces it provides.

We still have the option to deploying service providers on the class path (check out this example).

Deploying Services and Service Providers as modules

The Service Module

Let's say we have a service interface com.service.api.AnInterface. To declare this interface as service provider interface (SPI), we will use the "uses" clause in module-info.java:

module com.service.api {
    exports com.service.api;
    uses com.service.api.AnInterface;
}

The Service Provider Module

A service provider will use "provides .. with" clause to declare what service interface it intends to use (by using provides keyword) and what implementation of the interface it wants to expose (by using with keyword).

module com.service.provider {
    requires msg.service.api;
    provides com.service.api.AnInterface with com.service.provider.AnInterfaceImpl;
}

We don't have to specify the service implementation in a file under the resource directory META-INF/services. (Without modules, we still have to do that).

The Client Application using the Service

module com.example.app {
   requires com.service.api;
}

Client application should be unaware of the provider implementation at compile time. We can deploy any providers during runtime via 'module-path'.

Discovering the Service implementations

We still need to use ServiceLoader#load(AnInterface.class) to discover service implementation instances. This is typically done in service API module itself.

Let's see a complete example to understand how to do that.

Example

In this example, we will create very simple service API module 'msg.service.api' which 'uses' a service interface to display a message. The example Provider will implement the interface and will show the message in a Java Swing dialog. We will also create a third project which will require the service interface and will have the provider deployed during runtime via module path.

The Service API

msg.service.api/src/msg/service/MsgService.java

package msg.service;

import java.util.ArrayList;
import java.util.List;
import java.util.ServiceLoader;

public interface MsgService {

  static List<MsgService> getInstances() {
      ServiceLoader<MsgService> services = ServiceLoader.load(MsgService.class);
      List<MsgService> list = new ArrayList<>();
      services.iterator().forEachRemaining(list::add);
      return list;
  }

  void showMessage(String msg);
}

msg.service.api/src/module-info.java

module msg.service.api {
  exports msg.service;
  uses msg.service.MsgService;
}
D:\java-modules\java-service-loader-example\msg.service.api>tree /A /F
|
\---src
| module-info.java
|
\---msg
\---service
MsgService.java

Compiling the class:

D:\java-modules\java-service-loader-example\msg.service.api>javac -d out src/module-info.java src/msg/service/MsgService.java

Creating the jar:

D:\java-modules\java-service-loader-example\msg.service.api>jar --create --file msg-service.jar -C out .

Now we have following files:

D:\java-modules\java-service-loader-example\msg.service.api>tree /A /F
| msg-service.jar
|
+---out
| | module-info.class
| |
| \---msg
| \---service
| MsgService.class
|
\---src
| module-info.java
|
\---msg
\---service
MsgService.java

The Service Provider

msg.service.provider.swing/src/msg/provider/swing/MsgServiceImpl.java

package msg.provider.swing;

import msg.service.MsgService;
import javax.swing.*;

public class MsgServiceImpl implements MsgService{
  @Override
  public void showMessage(String msg) {
      JOptionPane.showMessageDialog(null, msg);
  }
}

msg.service.provider.swing/src/module-info.java

module msg.service.provider.swing {
  requires msg.service.api;
  requires java.desktop;
  provides msg.service.MsgService with msg.provider.swing.MsgServiceImpl;
}

Above module is also requiring 'java.desktop' which contains javax.swing.

Following is the directory structure of the provider project:

D:\java-modules\java-service-loader-example/msg.service.provider.swing>tree /A /F
|
\---src
| module-info.java
|
\---msg
\---provider
\---swing
MsgServiceImpl.java

Copying msg-lib.jar(the service API) to msg.service.provider.swing/lib, so that we can compile the provider classes:

D:\java-modules\java-service-loader-example\msg.service.provider.swing>mkdir lib
D:\java-modules\java-service-loader-example\msg.service.provider.swing>copy ..\msg.service.api\msg-service.jar lib\msg-service.jar
1 file(s) copied.
D:\java-modules\java-service-loader-example\msg.service.provider.swing>tree /A /F
|
+---lib
| msg-service.jar
|
\---src
| module-info.java
|
\---msg
\---provider
\---swing
MsgServiceImpl.java

Compiling the classes:

D:\java-modules\java-service-loader-example\msg.service.provider.swing>javac -d out --module-path lib src/module-info.java src/msg/provider/swing/MsgServiceImpl.java

Creating the jar:

D:\java-modules\java-service-loader-example\msg.service.provider.swing>jar --create --file msg-service-swing.jar -C out .
D:\java-modules\java-service-loader-example\msg.service.provider.swing>tree /A /F
| msg-service-swing.jar
|
+---lib
| msg-service.jar
|
+---out
| | module-info.class
| |
| \---msg
| \---provider
| \---swing
| MsgServiceImpl.class
|
\---src
| module-info.java
|
\---msg
\---provider
\---swing
MsgServiceImpl.java

The Service Client Application

my-app/src/com/logicbig/AppMain.java

package com.logicbig;

import msg.service.MsgService;

import java.util.List;

public class AppMain {
  public static void main(String[] args) {
      List<MsgService> msgServices = MsgService.getInstances();
      for (MsgService msgService : msgServices) {
          msgService.showMessage("A test message");
      }
  }
}

my-app/src/module-info.java

module my.app {
  requires msg.service.api;
}
D:\java-modules\java-service-loader-example/my-app>tree /A /F
|
\---src
| module-info.java
|
\---com
\---logicbig
AppMain.java

Copying msg-lib.jar (Service API) to my-app/lib/msg-lib.jar

D:\java-modules\java-service-loader-example\my-app>mkdir lib
D:\java-modules\java-service-loader-example\my-app>copy ..\msg.service.api\msg-service.jar lib\msg-service.jar
1 file(s) copied.

Copying msg-service-swing.jar (the provider) to my-app/lib/msg-service-swing.jar:

D:\java-modules\java-service-loader-example\my-app>copy ..\msg.service.provider.swing\msg-service-swing.jar lib\msg-service-swing.jar
1 file(s) copied.
D:\java-modules\java-service-loader-example\my-app>tree /A /F
|
+---lib
| msg-service-swing.jar
| msg-service.jar
|
\---src
| module-info.java
|
\---com
\---logicbig
AppMain.java

Compiling the classes:

D:\java-modules\java-service-loader-example\my-app>javac -d out --module-path lib src/module-info.java src/com/logicbig/AppMain.java

Running the application

D:\java-modules\java-service-loader-example\my-app>java --module-path out;lib --module my.app/com.logicbig.AppMain

Example Project

Dependencies and Technologies Used :

  • JDK 9
Java 9 Modules and Services Example Select All Download
  • java-service-loader-example
    • msg.service.api
      • src
        • msg
          • service
    • msg.service.provider.swing
      • src
        • msg
          • provider
            • swing
    • my-app
      • src
        • com
          • logicbig

See Also