Java Service Provider Interface (SPI), what is it and how to use it
Tags: java, javaunderthehood, tutorial, underthehood
Published: 2024-05-11 05:47:19

> As a software developer, it's not always possible to dive deep into the language you're working with, and sometimes it's difficult to find situations where you need to use advanced or non-trivial features. It's easy to use a magic framework, the hard part is having the necessary knowledge to create it. For this reason, I created the tag **#javaunderthehood** to gather this content and help other people understand how this magic happens. Feel free to join me with an interesting new topic.
  

 ---
## What is the Service Provider Interface (SPI)?
---
Introduced in Java 6, the Service Provider Interface (SPI) provides the ability to extend application functionality by adding service implementations that can modify, replace or extend the original behavior of an application.
 ---
## What is ServiceLoader java class?
---
The ServiceLoader class is the heart of this feature. What does the documentation say about the ServiceLoader class?

>A facility to load implementations of a service.
A service is a well-known interface or class for which zero, one, or many service providers exist. A service provider (or just provider) is a class that implements or subclasses the well-known interface or class. A ServiceLoader is an object that locates and loads service providers deployed in the run time environment at a time of an application's choosing. Application code refers only to the service, not to service providers, and is assumed to be capable of choosing between multiple service providers (based on the functionality they expose through the service), and handling the possibility that no service providers are located.

In general, the service is an interface that you can use to implement your own provider. If this service is part of a dependency of your project and this dependency gives you the possibility, you can write your own provider that fits your needs and the library will be able to use it.
 ---
## How to use it? 
---
Let's start by creating our service, which will be an interface that will allow us to create implementations to proccess different types of images.

```
public interface ImageProcessorService {  
    String getType();  
    String process();  
}
```

Now we will create two implementations of it, which will be responsible for proccessing PNG and JPG images.

**PNG**
```
public final class PNGImageProcessorProvider implements ImageProcessorService {
  @Override
  public String getType() {
    return BasicImageType.PNG.name();
  }

  @Override
  public void process() {
    System.out.println("Processing PNG image");
  }
}
```

**JPG**
```
public class JPGImageProcessorProvider implements ImageProcessorService {
  @Override
  public String getType() {
    return BasicImageType.JPG.name();
  }

  @Override
  public void process() {
    System.out.println("Processing JPG image");
  }
}
```
In order for ServiceLoader to load our provider, we need to create a package called **META-INF** in our **resource** repository. This folder must contain a subfolder called **services** where we need to create a file **with the reference to our service**.

The appearance of our project looks something like this at the moment:

```
.
├── java
│   └── com
│       └── github
│           └── c4rlosmonteiro
│               └── imageprocessorservicespi
│                   ├── BasicImageType.java
│                   ├── provider
│                   │   ├── JPGImageProcessorProvider.java
│                   │   └── PNGImageProcessorProvider.java
│                   └── service
│                       └── ImageProcessorService.java
└── resources
    └── META-INF
        └── services
            └── com.github.c4rlosmonteiro.imageprocessorservicespi.service.ImageProcessorService

```
Now you need to modify the file *com.github.c4rlosmonteiro.imageprocessorservicespi.service.ImageProcessorService* and add the references to all the providers you want to load. 
```
    com.github.c4rlosmonteiro.imageprocessorservicespi.provider.JPGImageProcessorProvider  
    com.github.c4rlosmonteiro.imageprocessorservicespi.provider.PNGImageProcessorProvider
```
To see the result of our implementation, we need to finally use the **ServiceLoader** class to load our providers.

```
public class ImageProcessorApp {
  private final List<ImageProcessorService> imageProcessorProviders;

  public ImageProcessorApp() {
    final ServiceLoader<ImageProcessorService> imageProcessorServiceLoader = ServiceLoader.load(ImageProcessorService.class);
    final Iterator<ImageProcessorService> imageProcessorServiceIterator = imageProcessorServiceLoader.iterator();

    this.imageProcessorProviders = new ArrayList<>();

    while (imageProcessorServiceIterator.hasNext()) {
      imageProcessorProviders.add(imageProcessorServiceIterator.next());
    }
  }

  public void processImages(final String type) {
    for (final ImageProcessorService imageProcessorProvider : imageProcessorProviders) {
      if (Objects.equals(imageProcessorProvider.getType(), type)) {
        imageProcessorProvider.process();
        return;
      }
    }

    throw new RuntimeException("No provider found to process image type: " + type + ". Create a new provider for it!");
  }
}
```
Finally, we create a simple main class and execute our code. 
```
public class Main {
  public static void main(final String[] args) {
    final ImageProcessorApp imageProcessorApp = new ImageProcessorApp();
    imageProcessorApp.processImages(BasicImageType.PNG.name());
    imageProcessorApp.processImages(BasicImageType.JPG.name());
    imageProcessorApp.processImages("MY_CUSTOM_OR_NOT_SUPPORTED_IMAGE_TYPE");
  }
}
```
Run and check the logs.

```
Processing PNG image
Processing JPG image
Exception in thread "main" java.lang.RuntimeException: No provider found to process image type: MY_CUSTOM_OR_NOT_SUPPORTED_IMAGE_TYPE. Create a new provider for it!
	at com.github.c4rlosmonteiro.imageprocessorservicespi.ImageProcessorApp.processImages(ImageProcessorApp.java:30)
	at com.github.c4rlosmonteiro.imageprocessorservicespi.Main.main(Main.java:8)

```
---

Check the source code on [github.com](https://github.com/c4rlosmonteiro/image-processor-service-spi).

---

> _Before you go!_
>
> _Follow for more **;)**_
> _Suggest new topics_
> _Use the comments section to discuss the feature and >comment on real-world use cases to help others understand >its usage_

---
_#javaunderthehood_