Close

Create Extensible Applications using Java ServiceLoader

[Last Updated: Sep 18, 2017]

The class java.util.ServiceLoader is based on SPI (Service Provider Interface) pattern.

SPI is an API intended to be implemented or extended by plugins/modules. Let's understand that with an example.

Assume we are designing an application framework F. We want F to be extensible. We will create API interfaces/classes visible to outside world and typically called Services to describe what F does. A particular application A will call the API to achieve a goal. A set of same interfaces/classes (SPI) of F are treated as extension points by plugins and modules. These plugins/modules are typically called Service Provider. They either provide concrete implementation or extend those interfaces/classes of the framework to provide functionality.


Discovering and Loading Services

The service provider must specify what implementation of a SPI they are providing. Suppose there's a service interface com.service.MyService and a provider implements that interface as com.provider.MyServiceImpl. Before packaging his project, the provider has to include a file under META-INF/services. The file name should be fully qualified service name without any extension. Inside file the provider has to specify fully qualified implementation. The content of file META-INF/services/com.service.MyService will look like this:

com.provider.MyServiceImpl
The SPI implementation is typically packaged in a jar and has to be in class path of the discovering code:
ServiceLoader<MyService> services =
                java.util.ServiceLoader.load(MyService.class);
services.forEach(service -> {//use service instance here
        }
);

The above service loading is typically performed in API framework F itself.

ServiceLoader implements java.lang.Iterable. We can iterate all service implementations and use them in whatever way we want to.

Example Project

In this example we are going a create a framework style Ui application. The application will discover the implementation of View interface and will display them as a JavaFx user interface. This API project itself provides an implementation DefaultView. Each discovered view implementation is displayed as a menu item. Clicking on the menu item shows the main view.

Dependencies and Technologies Used:

  • JDK 1.8
  • Maven 3.0.4

Java Service Loader Example Select All Download
  • java-view-service
    • src
      • main
        • java
          • com
            • logicbig
              • service
                • View.java
          • resources
            • META-INF
              • services

    Run the main class UiApplication either from your IDE or from command line as

    mvn clean install exec:java -Dexec.mainClass="com.logicbig.service.UiApplication"

    Here's our ui application:


    To demonstrate the extensibility concept, we are going to create another separate project which will import the view API and will provide one more view implementation without modifying original code base.

    Dependencies and Technologies Used:

    • com.logicbig.service:view-service 1.0-SNAPSHOT
    • JDK 1.8
    • Maven 3.0.4

    Service Provider Example Select All Download
    • simple-view-provider
      • src
        • main
          • java
            • com
              • logicbig
                • view
                  • SimpleView.java
            • resources
              • META-INF
                • services

      Note that, in this project we have included the original project dependency in pom.xml so you have to make sure that the first project has been install to your local maven repository by using
      mvn install.

      Now run the main class from the new project root

      d:\simple-view-provider>mvn clean install exec:java -Dexec.mainClass="com.logicbig.service.UiApplication"

      Here a new menu item 'Simple View' has been added to our base user interface. Clicking on that will show the new view.

      Regarding how should we add more view plugins and what project will run the main class. In this particular example there should be a separate assembler project which will have main API dependency and all view provider dependencies. That way we can easily manage views by adding and removing them.

      See Also