Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Java 9 Kickoff: Project Jigsaw and ServiceLoader (Part 2)

DZone's Guide to

Java 9 Kickoff: Project Jigsaw and ServiceLoader (Part 2)

Follow along to get a quick look at how to use ServiceLoader in JDK9.

· Java Zone
Free Resource

Download Microservices for Java Developers: A hands-on introduction to frameworks and containers. Brought to you in partnership with Red Hat.

Java is coming up with major changes and new surprises for developers. Similar major changes came in Java 5 and Java 8, but now Java 9 will come with brand new presents. In the last post, we discussed Java 9 with Jigsaw, and in another post, we were using ServiceLoader in Java instead of dependencies injections.

Image title


Java 9 gives us a clean way to use ServiceLoader in Java for maintaining DI using modularization. For details, please visit our previous posts as mentioned above. Today, we are creating a Greetings example using Jigsaw and ServiceLoader. Below is our directory structure of the sample application.

jigsaw

In this sample application, we will introduce two new packages com.knoldus.service and com.knoldus.service.provider.

Below is the conceptual diagram of how ServiceLoader works.

java-serviceloader

This works the same as DI frameworks work. Below are the steps and code of our sample application.

Step 1

Decide the modules in our application and define the abstract layer of the modules. Like in the above example, I have one service module that contains an abstract service layer for greetings. Below is our code:

package com.knoldus.service;

public interface GreetingsService {
    void sayHello(String name);
}


Our module-info.java code is below:

module com.knoldus.service {
    exports com.knoldus.service;
}


This module is the same as our other module, and we will just export our services. But the implementation of the module is not here. For the implementation of our abstract layer, we will create another module.

Step 2

Like in our previous step, we created an abstract layer module, but we still didn’t implement our abstract layer. For implementation, we need to create another module, which provides the implementation and maps the abstract service with its implementation and exports.

In our example, we are creating a com.knoldus.service.provider service provider module to define the implementation and mapping like below:

package com.knoldus.service.impl;

import com.knoldus.service.GreetingsService;

public class ConsoleGreetings implements GreetingsService {

    @Override
    public void sayHello(String name) {
        System.out.println("Hello to "+name);
    }
}

Our module-info.java is below:

module com.knoldus.service.provider {
    requires com.knoldus.service;

    provides com.knoldus.service.GreetingsService
    with com.knoldus.service.impl.ConsoleGreetings;
}


Step 3

The tricky part is in the module-info.java file. That file requires the service module with its implementation or via providing the implementation of the abstract layer using provides. These bindings are similar to Google Guice. 

If we need to change the implementation, there is no need to change our Java code — our Java code is the same, just change the module-info.java. The benefit of this approach is that we will also migrate our old Java code into a modular way without any side effects.

Another tricky part is in the consumer, which will consume the service. The information of the module is put in module-info.java file as shown below:

module com.knoldus.main {
    requires com.knoldus.util;
    requires com.knoldus.service;

    uses com.knoldus.service.GreetingsService;
}


In the above code, the consumer required the util and service modules, but still, we are not defining requirements for our provider module because, internally, the implementation is managed by ServiceLoader. Whenever consumers lookup the GreetingsService implementation, the ServiceLoader returns the implementation that is defined in our provider module.

How does the ServiceLoader know about the implementation? In module-info.java, we need to define our lookup declaration by using uses com.knoldus.service.GreetingsService. The ServiceLoader detects only those implementations that are defined using provides clause in provider module. If we are not defining the provider GreetingsService, it will throw an exception java.util.NoSuchElementException. Our lookup code is below in our consumer module:

package com.knoldus.providers;

import com.knoldus.service.GreetingsService;

import java.util.NoSuchElementException;
import java.util.ServiceLoader;

public class GreetingsProvider {

    private static GreetingsProvider provider;
    private ServiceLoader<GreetingsService> loader;

    private GreetingsProvider() {
        loader = ServiceLoader.load(GreetingsService.class);
    }

    public static GreetingsProvider getInstance() {
        if(provider == null) {
            provider = new GreetingsProvider();
        }
        return provider;
    }

    public GreetingsService serviceImpl() {
        GreetingsService service = loader.iterator().next();
        if(service != null) {
            return service;
        } else {
            throw new NoSuchElementException("No implementation for GreetingsProvider");
        }
    }
}


For the whole code, please download it here from GitHub.

Download Building Reactive Microservices in Java: Asynchronous and Event-Based Application Design. Brought to you in partnership with Red Hat

Topics:
java ,jigsaw ,serviceloader ,service

Published at DZone with permission of Harmeet Singh(Taara). See the original article here.

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}