eBook – Guide Spring Cloud – NPI EA (cat=Spring Cloud)
announcement - icon

Let's get started with a Microservice Architecture with Spring Cloud:

>> Join Pro and download the eBook

eBook – Mockito – NPI EA (tag = Mockito)
announcement - icon

Mocking is an essential part of unit testing, and the Mockito library makes it easy to write clean and intuitive unit tests for your Java code.

Get started with mocking and improve your application tests using our Mockito guide:

Download the eBook

eBook – Java Concurrency – NPI EA (cat=Java Concurrency)
announcement - icon

Handling concurrency in an application can be a tricky process with many potential pitfalls. A solid grasp of the fundamentals will go a long way to help minimize these issues.

Get started with understanding multi-threaded applications with our Java Concurrency guide:

>> Download the eBook

eBook – Reactive – NPI EA (cat=Reactive)
announcement - icon

Spring 5 added support for reactive programming with the Spring WebFlux module, which has been improved upon ever since. Get started with the Reactor project basics and reactive programming in Spring Boot:

>> Join Pro and download the eBook

eBook – Java Streams – NPI EA (cat=Java Streams)
announcement - icon

Since its introduction in Java 8, the Stream API has become a staple of Java development. The basic operations like iterating, filtering, mapping sequences of elements are deceptively simple to use.

But these can also be overused and fall into some common pitfalls.

To get a better understanding on how Streams work and how to combine them with other language features, check out our guide to Java Streams:

>> Join Pro and download the eBook

eBook – Jackson – NPI EA (cat=Jackson)
announcement - icon

Do JSON right with Jackson

Download the E-book

eBook – HTTP Client – NPI EA (cat=Http Client-Side)
announcement - icon

Get the most out of the Apache HTTP Client

Download the E-book

eBook – Maven – NPI EA (cat = Maven)
announcement - icon

Get Started with Apache Maven:

Download the E-book

eBook – Persistence – NPI EA (cat=Persistence)
announcement - icon

Working on getting your persistence layer right with Spring?

Explore the eBook

eBook – RwS – NPI EA (cat=Spring MVC)
announcement - icon

Building a REST API with Spring?

Download the E-book

Course – LS – NPI EA (cat=Jackson)
announcement - icon

Get started with Spring and Spring Boot, through the Learn Spring course:

>> LEARN SPRING
Course – RWSB – NPI EA (cat=REST)
announcement - icon

Explore Spring Boot 3 and Spring 6 in-depth through building a full REST API with the framework:

>> The New “REST With Spring Boot”

Course – LSS – NPI EA (cat=Spring Security)
announcement - icon

Yes, Spring Security can be complex, from the more advanced functionality within the Core to the deep OAuth support in the framework.

I built the security material as two full courses - Core and OAuth, to get practical with these more complex scenarios. We explore when and how to use each feature and code through it on the backing project.

You can explore the course here:

>> Learn Spring Security

Course – LSD – NPI EA (tag=Spring Data JPA)
announcement - icon

Spring Data JPA is a great way to handle the complexity of JPA with the powerful simplicity of Spring Boot.

Get started with Spring Data JPA through the guided reference course:

>> CHECK OUT THE COURSE

Partner – Moderne – NPI EA (cat=Spring Boot)
announcement - icon

Refactor Java code safely — and automatically — with OpenRewrite.

Refactoring big codebases by hand is slow, risky, and easy to put off. That’s where OpenRewrite comes in. The open-source framework for large-scale, automated code transformations helps teams modernize safely and consistently.

Each month, the creators and maintainers of OpenRewrite at Moderne run live, hands-on training sessions — one for newcomers and one for experienced users. You’ll see how recipes work, how to apply them across projects, and how to modernize code with confidence.

Join the next session, bring your questions, and learn how to automate the kind of work that usually eats your sprint time.

Course – LJB – NPI EA (cat = Core Java)
announcement - icon

Code your way through and build up a solid, practical foundation of Java:

>> Learn Java Basics

1. Overview

Java 6 has introduced a feature for discovering and loading implementations matching a given interface: Service Provider Interface (SPI).

In this tutorial, we’ll introduce the components of Java SPI and show how we can apply it to a practical use case.

2. Terms and Definitions of Java SPI

Java SPI defines four main components

2.1. Service

A well-known set of programming interfaces and classes that provide access to some specific application functionality or feature.

2.2. Service Provider Interface

An interface or abstract class that acts as a proxy or an endpoint to the service.

If the service is one interface, then it is the same as a service provider interface.

Service and SPI together are well-known in the Java Ecosystem as API.

2.3. Service Provider

A specific implementation of the SPI. The Service Provider contains one or more concrete classes that implement or extend the service type.

A Service Provider is configured and identified through a provider configuration file which we put in the resource directory META-INF/services. The file name is the fully-qualified name of the SPI and its content is the fully-qualified name of the SPI implementation.

The Service Provider is installed in the form of extensions, a jar file which we place in the application classpath, the Java extension classpath or the user-defined classpath.

2.4. ServiceLoader

At the heart of the SPI is the ServiceLoader class. This has the role of discovering and loading implementations lazily. It uses the context classpath to locate provider implementations and put them in an internal cache.

3. Differences Between SPI and API

In software development, we often see or hear another abbreviation that’s similar to SPI – an API (Application Programming Interface).

Let’s explore the key differences between them.

An API is a contract provided by a software library or framework that defines how other components, such as applications and modules, can interact with it. Therefore, an API is designed for developers using software to access its functionality, for example:

String me = "Kai";
String meUppercase = me.toUpperCase();
String threeStars = meUppercase.replaceAll("[A-Z]", "*");

In this example, the toUpperCase() and replaceAll() methods are from the Java String API. We can use these API-defined methods to access their functionalities, such as converting a String to uppercase and String substitution based on regex.

On the other hand, an SPI enables the creation of new functionality that integrates with the library or framework. It’s designed for developers creating extensions or custom implementations.

Simply put, an API provides an implementation of a contract, while an SPI provides the contract that we need to implement.

An analogy might help us understand their differences quickly. Let’s think of a web browser like Google Chrome. The buttons, such as “Back”, “Forward”, and “Refresh”, allow us to interact with the browser and view web pages. Thus, they belong to the API.

However, the browser extension/plugin design is SPI, as it allows third-party developers to create compatible extensions/plugins with different functionalities.

Java provides many SPIs. Here are some samples of the service provider interface and the service that it provides:

Next, let’s create an example application to understand SPI better.

4. Showcase: a Currency Exchange Rates Application

Now that we understand the basics, let’s describe the steps that are required to set up an exchange rate application.

To highlight these steps, we need to use at least three projects: exchange-rate-api, exchange-rate-impl, and exchange-rate-app.

We’ll cover the Service, the SPI and the ServiceLoader through the module exchange-rate-api, then we’ll implement our service provider in the exchange-rate-impl module, and finally, we’ll bring everything together through the module exchange-rate-app.

In fact, we can provide as many modules as we need for the service provider and make them available in the classpath of the module exchange-rate-app.

4.1. Building Our API

We start by creating a Maven project called exchange-rate-api. It’s good practice that the name ends with the term api, but we can call it whatever.

Then we create a model class for representing rates currencies:

package com.baeldung.rate.api;

public class Quote {
    private String currency;
    private BigDecimal ask;
    private BigDecinal bid;
    private LocalDate date;
    ...
}

And then we define our Service for retrieving quotes by creating the interface QuoteManager:

package com.baeldung.rate.api

public interface QuoteManager {
    List<Quote> getQuotes(String baseCurrency, LocalDate date);
}

Next, we create an SPI for our service:

package com.baeldung.rate.spi;

public interface ExchangeRateProvider {
    QuoteManager create();
}

And finally, we need to create a utility class ExchangeRate.java that can be used by client code. This class delegates to ServiceLoader.

First, we invoke the static factory method load() to get an instance of ServiceLoader:

ServiceLoader<ExchangeRateProvider> loader = ServiceLoader .load(ExchangeRateProvider.class);

And then we invoke the iterate() method to search and retrieve all available implementations.

Iterator<ExchangeRateProvider> = loader.iterator();

The search result is cached so we can invoke the ServiceLoader.reload() method in order to discover newly installed implementations:

Iterator<ExchangeRateProvider> = loader.reload();

And here’s our utility class:

public final class ExchangeRate {

    private static final String DEFAULT_PROVIDER = "com.baeldung.rate.spi.YahooFinanceExchangeRateProvider";

    //All providers
    public static List<ExchangeRateProvider> providers() {
        List<ExchangeRateProvider> services = new ArrayList<>();
        ServiceLoader<ExchangeRateProvider> loader = ServiceLoader.load(ExchangeRateProvider.class);
        loader.forEach(services::add);
        return services;
    }

    //Default provider
    public static ExchangeRateProvider provider() {
        return provider(DEFAULT_PROVIDER);
    }

    //provider by name
    public static ExchangeRateProvider provider(String providerName) {
        ServiceLoader<ExchangeRateProvider> loader = ServiceLoader.load(ExchangeRateProvider.class);
        Iterator<ExchangeRateProvider> it = loader.iterator();
        while (it.hasNext()) {
            ExchangeRateProvider provider = it.next();
            if (providerName.equals(provider.getClass().getName())) {
                return provider;
            }
        }
        throw new ProviderNotFoundException("Exchange Rate provider " + providerName + " not found");
    }
}

Now that we have a service for getting all installed implementations, we can use all of them in our client code to extend our application or just one by selecting a preferred implementation.

Note that this utility class is not required to be part of the api project. Client code can choose to invoke ServiceLoader methods itself.

4.2. Building the Service Provider

Let’s now create a Maven project named exchange-rate-impl and we add the API dependency to the pom.xml:

<dependency>
    <groupId>com.baeldung</groupId>
    <artifactId>exchange-rate-api</artifactId>
    <version>1.0.0-SNAPSHOT</version>
</dependency>

Then we create a class that implements our SPI:

public class YahooFinanceExchangeRateProvider 
  implements ExchangeRateProvider {
 
    @Override
    public QuoteManager create() {
        return new YahooQuoteManagerImpl();
    }
}

And here the implementation of the QuoteManager interface:

public class YahooQuoteManagerImpl implements QuoteManager {

    @Override
    public List<Quote> getQuotes(String baseCurrency, LocalDate date) {
        // fetch from Yahoo API
    }
}

In order to be discovered, we create a provider configuration file:

META-INF/services/com.baeldung.rate.spi.ExchangeRateProvider

The content of the file is the fully qualified class name of the SPI implementation:

com.baeldung.rate.impl.YahooFinanceExchangeRateProvider

4.3. Putting It Together

Finally, let’s create a client project called exchange-rate-app and add the dependency exchange-rate-api to the classpath:

<dependency>
    <groupId>com.baeldung</groupId>
    <artifactId>exchange-rate-api</artifactId>
    <version>1.0.0-SNAPSHOT</version>
</dependency>

At this point, we can call the SPI from our application:

ExchangeRate.providers().forEach(provider -> ... );

4.4. Running the Application

Let’s now focus on building all of our modules. Run this command in the root of the java-spi module:

mvn clean package

Then we run our application with the Java command without taking into account the provider. Run this command in the root of the java-spi module:

java -cp ./exchange-rate-api/target/exchange-rate-api-1.0.0-SNAPSHOT.jar:./exchange-rate-app/target/exchange-rate-app-1.0.0-SNAPSHOT.jar com.baeldung.rate.app.MainApp

The result of the above command will be empty because no providers was found.

Now we’ll include our provider in java.ext.dirs extension and we run the application again:

java -cp ./exchange-rate-api/target/exchange-rate-api-1.0.0-SNAPSHOT.jar:./exchange-rate-app/target/exchange-rate-app-1.0.0-SNAPSHOT.jar:./exchange-rate-impl/target/exchange-rate-impl-1.0.0-SNAPSHOT.jar:./exchange-rate-impl/target/depends/* com.baeldung.rate.app.MainApp

We can see that our provider is loaded and it will print the output of the ExchangeRate app.

NOTE: To provide multiple dependencies in the class path you have to choose between two different separators based on the operating system

  • For Linux, the separator is colon(:).
  • For Windows the separator is semicolon(;).

5. Conclusion

Now that we have explored the Java SPI mechanism through well-defined steps, it should be clear to see how to use the Java SPI to create easily extensible or replaceable modules.

Although our example used the Yahoo exchange rate service to show the power of plugging-in to other existing external APIs, production systems don’t need to rely on third-party APIs to create great SPI applications.

The code backing this article is available on GitHub. Once you're logged in as a Baeldung Pro Member, start learning and coding on the project.
Baeldung Pro – NPI EA (cat = Baeldung)
announcement - icon

Baeldung Pro comes with both absolutely No-Ads as well as finally with Dark Mode, for a clean learning experience:

>> Explore a clean Baeldung

Once the early-adopter seats are all used, the price will go up and stay at $33/year.

eBook – HTTP Client – NPI EA (cat=HTTP Client-Side)
announcement - icon

The Apache HTTP Client is a very robust library, suitable for both simple and advanced use cases when testing HTTP endpoints. Check out our guide covering basic request and response handling, as well as security, cookies, timeouts, and more:

>> Download the eBook

eBook – Java Concurrency – NPI EA (cat=Java Concurrency)
announcement - icon

Handling concurrency in an application can be a tricky process with many potential pitfalls. A solid grasp of the fundamentals will go a long way to help minimize these issues.

Get started with understanding multi-threaded applications with our Java Concurrency guide:

>> Download the eBook

eBook – Java Streams – NPI EA (cat=Java Streams)
announcement - icon

Since its introduction in Java 8, the Stream API has become a staple of Java development. The basic operations like iterating, filtering, mapping sequences of elements are deceptively simple to use.

But these can also be overused and fall into some common pitfalls.

To get a better understanding on how Streams work and how to combine them with other language features, check out our guide to Java Streams:

>> Join Pro and download the eBook

eBook – Persistence – NPI EA (cat=Persistence)
announcement - icon

Working on getting your persistence layer right with Spring?

Explore the eBook

Course – LS – NPI EA (cat=REST)

announcement - icon

Get started with Spring Boot and with core Spring, through the Learn Spring course:

>> CHECK OUT THE COURSE

Partner – Moderne – NPI EA (tag=Refactoring)
announcement - icon

Modern Java teams move fast — but codebases don’t always keep up. Frameworks change, dependencies drift, and tech debt builds until it starts to drag on delivery. OpenRewrite was built to fix that: an open-source refactoring engine that automates repetitive code changes while keeping developer intent intact.

The monthly training series, led by the creators and maintainers of OpenRewrite at Moderne, walks through real-world migrations and modernization patterns. Whether you’re new to recipes or ready to write your own, you’ll learn practical ways to refactor safely and at scale.

If you’ve ever wished refactoring felt as natural — and as fast — as writing code, this is a good place to start.

Course – LS – NPI (cat=Java)
announcement - icon

Get started with Spring Boot and with core Spring, through the Learn Spring course:

>> CHECK OUT THE COURSE

eBook Jackson – NPI EA – 3 (cat = Jackson)