How Java SPI Powers Plug‑In Song Parsers: Build an Extensible Framework in Minutes

This article walks through creating a generic song‑parsing framework using Java's Service Provider Interface (SPI), showing the evolution from a hard‑coded parser to a modular, plug‑in architecture that supports adding new formats like MP3, MP4, and RMVB without changing core code.

Java Backend Technology
Java Backend Technology
Java Backend Technology
How Java SPI Powers Plug‑In Song Parsers: Build an Extensible Framework in Minutes

Background

After joining the architecture team, Xiaohei was assigned to develop a generic song‑information parsing framework that, given raw song data, returns the song name, author, duration, and other metadata.

First Two Versions

The initial implementation exposed static methods for each format:

public class ParseUtil {
    public static Song parseMp3Song(byte[] data) {
        // parse song according to mp3 data format
    }
}

Later a second version added a method for MP4:

public class ParseUtil {
    public static Song parseMp4Song(byte[] data) {
        // parse song according to mp4 data format
    }
}

Both versions required callers to know the exact format and invoke the corresponding method, which quickly became unmaintainable.

Inspiration from JDBC

Observing that JDBC hides driver details behind a common API, Xiaohei decided to apply the same Service Provider Interface (SPI) pattern to the song parser.

Modular Design (Version 3.0)

song-parser – defines the generic Parser interface and the ParserManager utility.

song-parser-mp3 – provides an MP3 implementation.

song-parser-mp4 – provides an MP4 implementation.

Clients only need to add the desired implementation JARs as Maven dependencies:

<dependency>
    <groupId>com.chenshuyi.demo</groupId>
    <artifactId>song-parser</artifactId>
    <version>1.0.0</version>
</dependency>

<dependency>
    <groupId>com.xiaohei.demo</groupId>
    <artifactId>song-parser-mp3</artifactId>
    <version>1.0.0</version>
</dependency>

Parsing is performed with a single call:

Song song = ParserManager.getSong(mockSongData("MP3"));

Core SPI Components

Parser Interface

public interface Parser {
    Song parse(byte[] data) throws Exception;
}

ParserManager

public class ParserManager {
    private static final CopyOnWriteArrayList<ParserInfo> registeredParsers = new CopyOnWriteArrayList<>();

    static { loadInitialParsers(); System.out.println("SongParser initialized"); }

    private static void loadInitialParsers() {
        ServiceLoader<Parser> loadedParsers = ServiceLoader.load(Parser.class);
        Iterator<Parser> it = loadedParsers.iterator();
        try { while (it.hasNext()) { it.next(); } } catch (Throwable t) { /* ignore */ }
    }

    public static synchronized void registerParser(Parser parser) {
        registeredParsers.add(new ParserInfo(parser));
    }

    public static Song getSong(byte[] data) {
        for (ParserInfo info : registeredParsers) {
            try {
                Song song = info.parser.parse(data);
                if (song != null) { return song; }
            } catch (Exception e) { /* wrong parser, ignore */ }
        }
        throw new ParserNotFoundException("10001", "Can not find corresponding data:" + new String(data));
    }
}

The loadInitialParsers method uses ServiceLoader to discover all Parser implementations declared in META-INF/services/com.chenshuyi.demo.Parser files.

Adding a New Format (RMVB)

To support a new format, create a separate module:

<dependency>
    <groupId>com.anonymous.demo</groupId>
    <artifactId>song-parser-rmvb</artifactId>
    <version>1.0.0</version>
</dependency>

Implement the parser:

public class RmvbParser implements com.chenshuyi.demo.Parser {
    public final byte[] FORMAT = "RMVB".getBytes();
    public final int FORMAT_LENGTH = FORMAT.length;

    @Override
    public Song parse(byte[] data) throws Exception {
        if (!isDataCompatible(data)) { throw new Exception("data format is wrong."); }
        return new Song("AGA", "rmvb", "《Wonderful U》", 240L);
    }

    private boolean isDataCompatible(byte[] data) {
        byte[] format = Arrays.copyOfRange(data, 0, FORMAT_LENGTH);
        return Arrays.equals(format, FORMAT);
    }
}

Register the parser at class loading time:

public class Parser {
    static {
        try { ParserManager.registerParser(new RmvbParser()); }
        catch (Exception e) { throw new RuntimeException("Can't register parser!"); }
    }
}

Provide a service descriptor file resources/META-INF/services/com.chenshuyi.demo.Parser containing:

com.anonymous.demo.RmvbParser

After adding the dependency, the same ParserManager.getSong call can parse RMVB data:

Song song = ParserManager.getSong(mockSongData("RMVB"));
System.out.println("Name:" + song.getName());
System.out.println("Author:" + song.getAuthor());
System.out.println("Time:" + song.getTime());
System.out.println("Format:" + song.getFormat());

Output:

Name:《Wonderful U》
Author:AGA
Time:240
Format:rmvb

Conclusion

The Java SPI mechanism enables a clean, plug‑in architecture where new parsers can be added simply by providing an implementation JAR and a service descriptor, without modifying the core framework. This pattern mirrors JDBC and other Java libraries, offering a scalable way to extend backend functionality.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaBackend Developmentplugin architectureSPIframework designServiceLoader
Java Backend Technology
Written by

Java Backend Technology

Focus on Java-related technologies: SSM, Spring ecosystem, microservices, MySQL, MyCat, clustering, distributed systems, middleware, Linux, networking, multithreading. Occasionally cover DevOps tools like Jenkins, Nexus, Docker, and ELK. Also share technical insights from time to time, committed to Java full-stack development!

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.