0Pricing
Java Academy · Lesson

Services with provides/uses

Decouple with the service loader.

Services with provides/uses is a free Java Academy lesson on CoddyKit — lesson 3 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the Java Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.

Decoupling with Services

Modules can collaborate without depending on each other's implementations using the service loader pattern.

A module declares it uses a service interface; another declares it provides an implementation. The JVM wires them together.

The Three Roles

A service has three participants:

  • Service interface: the contract, in some module
  • Provider: a module supplying an implementation
  • Consumer: a module that loads implementations at run time

The Service Interface

First define the contract. It is just a normal interface in an exported package so both providers and consumers can reference it.

package com.example.spi;

public interface Codec {
    String name();
    byte[] encode(String input);
}

Consumer Declares uses

The consumer module declares it uses the interface. This tells JPMS the module will look up implementations via ServiceLoader.

module com.example.app {
    requires com.example.spi;
    uses com.example.spi.Codec;
}

Provider Declares provides

A provider module declares which interface it implements with provides ... with ..., naming its concrete class.

module com.example.base64 {
    requires com.example.spi;
    provides com.example.spi.Codec
        with com.example.base64.Base64Codec;
}

The Provider Implementation

The implementation class is ordinary Java. It must be public with either a public no-arg constructor or a public static provider() method.

package com.example.base64;

import com.example.spi.Codec;
import java.util.Base64;

public class Base64Codec implements Codec {
    public String name() { return "base64"; }
    public byte[] encode(String input) {
        return Base64.getEncoder().encode(input.getBytes());
    }
}

Loading with ServiceLoader

At run time the consumer asks ServiceLoader.load(Codec.class) for all available providers and iterates them, with no compile-time link to any implementation.

import com.example.spi.Codec;
import java.util.ServiceLoader;

public class App {
    public static void main(String[] args) {
        ServiceLoader<Codec> loader = ServiceLoader.load(Codec.class);
        for (Codec codec : loader) {
            System.out.println("Found codec: " + codec.name());
        }
    }
}

Why This Is Powerful

The consumer never names a provider class. Drop a new provider module on the module path and it is discovered automatically.

This enables plugin architectures and clean inversion of dependencies.

The provider() Factory

If construction needs logic, expose a public static provider() method instead of a no-arg constructor. JPMS calls it to obtain the instance.

package com.example.base64;

import com.example.spi.Codec;

public class CodecFactory {
    public static Codec provider() {
        return new Base64Codec();
    }
}

Multiple Providers

Many modules can each provide the same interface. ServiceLoader returns all of them, letting your consumer pick by name, priority, or capability.

Add or remove providers without touching consumer code.

Stream Over Providers

Since Java 9, ServiceLoader offers stream(), yielding Provider handles you can filter before instantiating, useful when construction is expensive.

import com.example.spi.Codec;
import java.util.ServiceLoader;

public class App {
    public static void main(String[] args) {
        Codec chosen = ServiceLoader.load(Codec.class).stream()
            .map(ServiceLoader.Provider::get)
            .filter(c -> c.name().equals("base64"))
            .findFirst()
            .orElseThrow();
        System.out.println("Using: " + chosen.name());
    }
}

Quick Check

Recall which directive each role uses.

Recap

You learned the module service pattern:

  • Three roles: service interface, provider, consumer
  • Consumer: uses; provider: provides ... with
  • Implementations loaded at run time via ServiceLoader
  • Providers need a public no-arg constructor or static provider()

Next: migrating an existing project to modules.

Frequently asked questions

Is the “Services with provides/uses” lesson free?

Yes — the full text of “Services with provides/uses” is free to read here on the web, and the Java Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the Java Academy course, upgrade to CoddyKit PRO.

What will I learn in “Services with provides/uses”?

Decouple with the service loader. You practise Java Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.

Do I need any experience to start Java Academy?

No prior experience is required. Java Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 3 of 4, so you can start here or from the beginning and move at your own pace.

How long does the “Services with provides/uses” lesson take?

Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.

Can I write and run code in this Java Academy lesson?

Yes. Every Java Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.

All lessons in this course

  1. module-info.java
  2. requires and exports
  3. Services with provides/uses
  4. Migrating to Modules
← Back to Java Academy