Mastering Java Plugin Architecture: From SPI to Spring Factories

This article explains how to implement plugin mechanisms in Java using SPI and Spring Boot's spring.factories, covering benefits, common approaches, detailed code examples, custom configuration loading, and a complete real‑world case study to guide developers in building extensible applications.

Top Architect
Top Architect
Top Architect
Mastering Java Plugin Architecture: From SPI to Spring Factories

1. Introduction

Plugins enable module decoupling, improve extensibility, and simplify third‑party integration. In Java, several mechanisms such as the standard Service Provider Interface (SPI) and Spring Boot's spring.factories can be used to achieve this.

2. Common Plugin Implementation Ideas

2.1 Benefits of Plugins

Module decoupling

Improved extensibility and openness

Easy third‑party integration

2.2 Typical Java Implementations

SPI (ServiceLoader)

Custom configuration with reflection

Loading external JARs at runtime

3. Java SPI Implementation

Define a service interface and provide concrete implementations. The interface is placed in a shared JAR, while each implementation is packaged in its own JAR with a service descriptor.

public interface MessagePlugin {
    String sendMsg(Map<String, Object> msgMap);
}

Two example implementations:

public class AliyunMsg implements MessagePlugin {
    @Override
    public String sendMsg(Map<String, Object> msgMap) {
        System.out.println("aliyun sendMsg");
        return "aliyun sendMsg";
    }
}

public class TencentMsg implements MessagePlugin {
    @Override
    public String sendMsg(Map<String, Object> msgMap) {
        System.out.println("tencent sendMsg");
        return "tencent sendMsg";
    }
}

In META-INF/services/com.example.MessagePlugin list the implementation class names:

com.example.AliyunMsg
com.example.TencentMsg

Load and invoke the plugins with ServiceLoader:

ServiceLoader<MessagePlugin> loader = ServiceLoader.load(MessagePlugin.class);
for (MessagePlugin plugin : loader) {
    plugin.sendMsg(new HashMap<>());
}
SPI diagram
SPI diagram

4. Custom Configuration Loading

Sometimes the implementation JARs are not known at compile time. A custom configuration file can list the fully qualified class names, which are then loaded via reflection or a URLClassLoader.

public class ClassImpl {
    private String name;
    private String[] clazz;
    // getters and setters
}

Example YAML/Properties:

server:
  port: 8081
impl:
  name: com.example.MessagePlugin
  clazz:
    - com.example.AliyunMsg
    - com.example.TencentMsg

Loading logic (simplified):

for (String className : classImpl.getClazz()) {
    Class<?> cls = Class.forName(className);
    MessagePlugin plugin = (MessagePlugin) cls.getDeclaredConstructor().newInstance();
    plugin.sendMsg(new HashMap<>());
}

5. Spring Boot Plugin Mechanism

Spring Boot extends the SPI idea with spring.factories. Define an interface, provide implementations, and list them in META-INF/spring.factories. Spring’s SpringFactoriesLoader reads the file and creates instances.

public interface SmsPlugin {
    void sendMessage(String message);
}

Two implementations:

public class BizSmsImpl implements SmsPlugin {
    @Override
    public void sendMessage(String message) {
        System.out.println("this is BizSmsImpl sendMessage..." + message);
    }
}

public class SystemSmsImpl implements SmsPlugin {
    @Override
    public void sendMessage(String message) {
        System.out.println("this is SystemSmsImpl sendMessage..." + message);
    }
}

spring.factories entry:

com.example.SmsPlugin=\
com.example.SystemSmsImpl,\
com.example.BizSmsImpl

Load and use the plugins:

@GetMapping("/sendMsgV3")
public String sendMsgV3(String msg) {
    List<SmsPlugin> smsServices = SpringFactoriesLoader.loadFactories(SmsPlugin.class, null);
    for (SmsPlugin s : smsServices) {
        s.sendMessage(msg);
    }
    return "success";
}
Spring Factories output
Spring Factories output

6. Full Case Study

The article presents a realistic scenario with three micro‑services:

biz‑pp : defines MessagePlugin and publishes it as a JAR.

bitpt : Aliyun SMS implementation.

miz‑pt : Tencent SMS implementation.

The core module provides a PluginFactory that selects the appropriate implementation based on a configuration property ( msg.type=aliyun or tencent). A Spring Boot controller calls the factory, and the selected plugin sends the message.

public static MessagePlugin getTargetPlugin(String type) {
    ServiceLoader<MessagePlugin> loader = ServiceLoader.load(MessagePlugin.class);
    for (MessagePlugin p : loader) {
        if (type.equals("aliyun") && p instanceof BitptImpl) return p;
        if (type.equals("tencent") && p instanceof MizptImpl) return p;
    }
    return null; // fallback to default implementation
}

Maven dependencies include the core module and the two implementation modules. After packaging and starting the application, a request to localhost:8087/sendMsg?msg=hello triggers the selected SMS provider, as shown in the console output.

Demo output
Demo output

7. Conclusion

Plugin mechanisms are pervasive across languages, frameworks, and middleware. Understanding Java SPI, custom reflective loading, and Spring Boot’s spring.factories equips developers to design extensible, maintainable systems and to adapt quickly to changing business requirements.

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.

Javaplugin architectureSpring Bootdependency-injectionSPISpring Factories
Top Architect
Written by

Top Architect

Top Architect focuses on sharing practical architecture knowledge, covering enterprise, system, website, large‑scale distributed, and high‑availability architectures, plus architecture adjustments using internet technologies. We welcome idea‑driven, sharing‑oriented architects to exchange and learn together.

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.