Overview

Java offers various features to provide more functionality to its developers. One such feature is Java service loader class. In this article, we will be further discussing the working of Java service loader, understanding how it works, and how developers can implement it in their Java codes.

Java service loader

The Java Service Loader is a simple way to use the built-in Java mechanism for dynamically loading an interface along with implementations. With the Java Service Loader, interface loading with implementation becomes natural and more convenient, making programs to be easily extended.

All the interfaces used in an application are commonly called services. They just define what can be done but not how it can be done. A single interface can have various implementations and each of such implementations is called a service provider. Service Loader uses both of these concepts to link a particular implementation with the interface.

Why would you use the Java service loader class?

Introduced in Java 1.6, the Java Service Loader was a great improvement in Java. It works by allowing developers to perform instantiation. It also provides a simple dependency injection mechanism that can be built in the Java SE.

Java Service Loader class allows developers to link an interface with a particular implementation at run time according to the provided configuration. This is a good way of developing loosely coupled applications. It also provides Dependency Injection (DI) though not as significant as the Spring framework but can be used in case you do not need a fully functional Dependency Injection feature.

Another benefit is that various design patterns such as Strategy can be easily done with the help of Service Loader. With the Java Service Loader, interface and implementation separation also become very natural that allowing to extent programs very conveniently.

What is a provider configuration file?

To link a specific implementation with an interface, for instance, linking service with a service provider makes use of a provider configuration file. This file contains the mapping of the interface to implementation and is presented in the resource directory META-INF/services. The provider configuration file also contains a list of fully-qualified binary names of concrete provider classes, one in each line, and is compliant with UTF -8.

Working with Java service loader

Java service loader class and services allow developers to design and implement extensible applications without making any changes to the original codebase. Although this can be achieved using dependency injection or inversion of control frameworks, we will be focusing on the Java Service loader as it offers the native solution.

Most of the Java APIs are implemented using the Service Loader. The class java. util.ServiceLoader is based on the design pattern of SPI (Service Provider Interface). It is an API intended to be implemented or extended by plugins/modules in Java.

Let’s build an extensible application using the SPI pattern. Consider a scenario where a fictitious online store allows its customers to choose from a list of payment service providers to be used on their site. The platform can be coded against a payment service interface with a mechanism to load the chosen payment service provider. Developers and vendors can now offer payment features with one or more specific implementations.

 

We will start with defining a payment service interface:

1. package com.mystore.payment.spi;
2. public interface myService {
3.     Result charge(Invoice invoice);
4. }

 

At some point during the platform’s startup, a payment service will be requested from the Java Service Loader class using a similar code like this:

1. import java.util.Optional;
2. import java.util.ServiceLoader;
3. 
4. import com.mystore.payment.spi;
5. 
6. Optional<myService> loadMyService() {
7.     return ServiceLoader
8.             .load(myService.class)
9.             .findFirst();
10. }

Here, the default load() method in the Java Service Loader class searches the application classpath with the default class loader. You can also use the overloaded load() method to pass a custom class loader to implement a bit more specific searches for service providers. The service providers should also implement the service interface for the Service Loader to locate service providers. In this case, it is the myService interface.

 

See this service provider example below:

1. package com.mystore.payment.stripe;
2. 
3. public class myService implements myService {
4. 
5.     @Override
6.     public Result charge(Invoice invoice) {
7.         // Returns the result.
8.         ...
9.         return new Result.Builder()
10.                 .build();
11.     }
12. }

Now, the service provider should register itself by creating a provider configuration file discussed before, the name of the configuration file must be the fully qualified class name of the service provider, where each component of the name must be separated by a period “.”.

 

To register the myService as a service provider we have to create a file named “com.mystore.payment.spi.payment” and add the line mentioned below:

com.mycommerce.payment.stripe.myService

Using this tutorial setup and configuration, you can now load new service providers without any code changes. Following this pattern allows you to build extensible applications with ease.

Implementing the Interface using Java service loader:

Now let’s look into how to implement the interface with the Java service loader. When working with, the basic concepts include:

  • Operating on interfaces of services
  • Obtaining implementation(s) of the service via Service Loader
  • Providing implementation of services

Start with the interface and put it in a jar file, named demo.jar

1. package demo;
2. 
3. public interface demoService {
4.
5.   int getResult();
6. }

 

After that, we will provide an implementation of that service in the jar file named demo-impl.jar, containing an implementation of the service:

1. package demo.impl;
2. import demo.demoService;
3.
4. public interface DefaultdemoService implements demoService {
5.   public long getResult() {
6.     return result();
7.   }
8.   private int result(){
9.     ...
10.  }
11. }

later, the demo-impl.jar has a file declaring that this jar file provides an implementation of demoService.

 

The file must have a path starting with META-INF/services/ and must have the same name as the fully-qualified name of the interface:

META-INF/services/demo.demoService

 

The content of the file is the fully-qualified name of the implementation:

demo.impl.demoService

 

when both jar files are in the class path of the program, this consumes the demoService, an instance of this service can be obtained by using a Service Loader

1. ServiceLoader<demoService> loader = ServiceLoader.load(demoService.class)
2. demoService service = loader.next();
3. int result = service.getresult();

 

being an Iterable, the Service Loader supports multiple implementation providers, where the program can choose from:

1. ServiceLoader<demoService> loader = ServiceLoader.load(demoService.class)
2. for(demoService service : loader) {
3. ...
4. }

Note that when next() will be invoked, a new instance will be created every time. If you wish to just re-use an instance, you will then have to use the iterator() method of the Service Loader or the for each loop as shown above in the example.

See Also: Accelerating Java With GPUs

Conclusion

We discussed how the service loading mechanism works in Java using the SPI pattern. Along with that, we looked into how an interface can be implemented. The best application of Java service loader is extending your code without making changes in the original code. If used correctly, it can give immense freedom to developers to keep extending their applications very conveniently.

Author

Shaharyar Lalani is a developer with a strong interest in business analysis, project management, and UX design. He writes and teaches extensively on themes current in the world of web and app development, especially in Java technology.

Write A Comment