Java 9 Modules - Service provider module with public static provider() method

[Updated: Oct 11, 2017, Created: Oct 11, 2017]

Java 9 provides another option for the service provide module, that is to provide a class with public static method named 'provider()' which can return the service implementation.

From java.util.ServiceLoader API docs:

A service provider that is developed in a module has no control over when it is instantiated, since that occurs at the behest of the application, but it does have control over how it is instantiated:

  • If the service provider declares a provider method, then the service loader invokes that method to obtain an instance of the service provider. A provider method is a public static method named "provider" with no formal parameters and a return type that is assignable to the service's interface or class.

    In this case, the service provider itself need not be assignable to the service's interface or class.

With this option provider's module-info.java will look like:

module msg.service.provider.swing {
   ......
   provides service.api.AService with service.provider.TheServiceProvider;
   ...
}

Where TheServiceProvider class has the provider() method with following signature:

 ......
public class TheServiceProvider {
   public static AService provider() {
       return new AServiceImpl();
   }
 ......
}

This option can be preferable for the scenarios where we want to have more control over how the service implementation is initiated. For example, we can initialize the service implementation with some dynamic information or since we can have the service implementation instance handle, we can use or pass it to some other component/observer etc.

Example

Here we are going to reuse our last example. We will just need to modify our provider module, the rest is going to be same.

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;
}

Compiling the class:

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

Creating the jar:

D:\service-provider-method-example\msg.service.api>jar --create --file msg-service.jar -C out .

The Service Provider

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

package msg.provider.swing;

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

public class MsgServiceProvider {

    public static MsgService provider() {
        return new MsgServiceProviderImpl();
    }

    private static class MsgServiceProviderImpl implements MsgService {
        @Override
        public void showMessage(String msg) {
            JOptionPane.showMessageDialog(null, "From Msg Provider: " + 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.MsgServiceProvider;
}

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

D:\service-provider-method-example\msg.service.provider.swing>mkdir lib
D:\service-provider-method-example\msg.service.provider.swing>copy ..\msg.service.api\msg-service.jar lib\msg-service.jar
1 file(s) copied.

Compiling the classes:

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

Creating the jar:

D:\service-provider-method-example\msg.service.provider.swing>jar --create --file msg-service-swing.jar -C out .

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;
}

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

D:\service-provider-method-example\my-app>mkdir lib
D:\service-provider-method-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:\service-provider-method-example\my-app>copy ..\msg.service.provider.swing\msg-service-swing.jar lib\msg-service-swing.jar
1 file(s) copied.
D:\service-provider-method-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:\service-provider-method-example\my-app>javac -d out --module-path lib src/module-info.java src/com/logicbig/AppMain.java

Running the application

D:\service-provider-method-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
  • service-provider-method-example
    • msg.service.api
      • src
        • msg
          • service
    • msg.service.provider.swing
      • src
        • msg
          • provider
            • swing
    • my-app
      • src
        • com
          • logicbig

See Also