Service Loading in Java

The ServiceLoader class (see Chapter 6 of Volume I) provides a lightweight mechanism for matching up service interfaces with implementations. The Java Platform Module System makes this mechanism easier to use.

Here is a quick reminder of service loading. A service has an interface and one or more possible implementations. Here is a simple example of an interface:

public interface GreeterService


String greet(String subject);

Locale getLocale();


One or more modules provide implementations, such as

public class FrenchGreeter implements GreeterService


public String greet(String subject) { return “Bonjour ” + subject; }

public Locale getLocale() { return Locale.FRENCH; }


The service consumer must pick an implementation among all offered implementations, based on whatever criteria it deems appropriate.

ServiceLoader<GreeterService> greeterLoader = ServiceLoader.load(GreeterService.class);

GreeterService chosenGreeter;

for (GreeterService greeter : greeterLoader)


if (. . .)


chosenGreeter = greeter;



In the past, implementations were offered by placing text files into the META-INF/services directory of the JAR file containing the implementation classes. The module system provides a better approach. Instead of text files, you can add statements to the module descriptors.

A module providing an implementation of a service adds a provides statement that lists the service interface (which may be defined in any module) and the implementing class (which must be a part of this module). Here is an example from the module:






This is the equivalent of the META-INF/services file.

A consuming module contains a uses statement.

module java.base




When code in a consuming module calls ServiceLoader.load(Servicelnterface.class), the matching provider classes will be loaded, even though they may not be in accessible packages.

In our code example, we provide implementations for a German and French greeter in the package com.horstmann.greetsvc.internal. The service module exports the com.horstmann.greetsvc package, but not the package with the implementations. The provides statement declares the service and its implementing classes in the unexported package:

module com.horstmann.greetsvc


exports com.horstmann.greetsvc;

provides com.horstmann.greetsvc.GreeterService with




The v2ch09.useservice module consumes the service. Using the ServiceLoader facility, we iterate over the provided services and pick the one matching the desired language:

package com.horstmann.hello;

import java.util.*;

import com.horstmann.greetsvc.*;

public class HelloWorld


public static void main(String[] args)


ServiceLoader<GreeterService> greeterLoader

= ServiceLoader.load(GreeterService.class);

String desiredLanguage = args.length > 0 ? args[0] : “de”;

GreeterService chosenGreeter = null;

for (GreeterService greeter : greeterLoader)


if (greeter.getLocale().getLanguage().equals(desiredLanguage))

chosenGreeter = greeter;


if (chosenGreeter == null)

System.out.println(“No suitable greeter.”);


System.out.println(chosenGreeter.greet(“Modular World”));



The module declaration requires the service module and declares that the GreeterService is being used.

module v2ch09.useservice


requires com.horstmann.greetsvc;

uses com.horstmann.greetsvc.GreeterService;


As a result of the provides and uses declarations, the module that consumes the service is allowed access to the private implementation classes.

To build and run the program, first compile the service:

javac com.horstmann.greetsvc/ \

com.horstmann.greetsvc/com/horstmann/greetsvc/ \


Then compile and run the consuming module:

javac -p com.horstmann.greetsvc \

v2ch09.useservice/com/horstmann/hello/ \


java -p com.horstmann.greetsvc:v2ch09.useservice \

-m v2ch09.useservice/com.horstmann.hello.HelloWorld

Source: Horstmann Cay S. (2019), Core Java. Volume II – Advanced Features, Pearson; 11th edition.

Leave a Reply

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