Java SPI Vs @Autoservice

Java SPI (Service Provider Interface) is a design pattern that typically use the same interface for multiple services and has the service implement that interface.

Use case: let the user choose service of the same type they wanna use. For example:

  • Database Driver
  • Logging library / type, etc..

We can use ServiceLoader to load the correct service.

For example:

// Core module
public interface MyService {
    void doSomething();
}

// Implementation module 1
public class MyServiceImpl1 implements MyService {
    @Override
    public void doSomething() {
        // Implementation 1
    }
}

// Implementation module 2
public class MyServiceImpl2 implements MyService {
    @Override
    public void doSomething() {
        // Implementation 2
    }
}

// META-INF/services/com.example.MyService
// Contains:
// com.example.MyServiceImpl1
// com.example.MyServiceImpl2

// Application code
ServiceLoader<MyService> serviceLoader = ServiceLoader.load(MyService.class);
for (MyService service : serviceLoader) {
    service.doSomething(); // Dynamically uses implementations
}

Note: for this to work, we have to manually create the META-INF for our service configuration which contains the package for our services.

@AutoService

Google @AutoService aim to help us with the configuration creation which will automatically create the services for us:

annotationProcessor 'com.google.auto.service:auto-service:1.1.1'  
implementation 'com.google.auto.service:auto-service:1.1.1'

In the code we can then just do

// Core module
public interface MyService {
    void doSomething();
}

// Implementation module 1
@AutoService(MyService.class)
public class MyServiceImpl1 implements MyService {
    @Override
    public void doSomething() {
        // Implementation 1
    }
}

// Implementation module 2
@AutoService(MyService.class)
public class MyServiceImpl2 implements MyService {
    @Override
    public void doSomething() {
        // Implementation 2
    }
}

// Application code
ServiceLoader<MyService> serviceLoader = ServiceLoader.load(MyService.class);
for (MyService service : serviceLoader) {
    service.doSomething(); // Dynamically uses implementations
}

@AutoService will automatically create the META-INF for us and put it inside the build/ folder