Java Plugin Development: SPI, ServiceLoader, Custom Configurations, and Spring Boot Factories
This article explains the concept and benefits of plugin-based development, outlines common Java implementation approaches such as SPI, configuration‑driven reflection, runtime JAR loading, and Spring Boot's spring.factories, and provides detailed code examples and a complete case study demonstrating flexible, hot‑pluggable SMS services.
Plugin‑based development is widely used in many programming languages and frameworks, such as Jenkins, Rancher, and IDEs like IDEA and VSCode, providing modularity, extensibility, and ease of third‑party integration.
The article first discusses the advantages of plugins, including module decoupling, improved extensibility, and convenient third‑party access, and then lists common implementation ideas in Java, such as SPI, configuration‑driven reflection, Spring Boot factories, Java agents, and third‑party plugin packages.
It then presents concrete Java examples: a ServiceLoader‑based SPI implementation with an interface public interface MessagePlugin { String sendMsg(Map msgMap); } and two implementations public class AliyunMsg implements MessagePlugin { @Override public String sendMsg(Map msgMap) { System.out.println("aliyun sendMsg"); return "aliyun sendMsg"; } } and public class TencentMsg implements MessagePlugin { @Override public String sendMsg(Map msgMap) { System.out.println("tencent sendMsg"); return "tencent sendMsg"; } } , showing the required META‑INF/services file, the loader code, and a Spring Boot controller that invokes the plugins.
Next, a custom configuration approach is described, where implementation class names are placed in a YAML/properties file, loaded via reflection, and instantiated dynamically; the full code for the configuration class, loader utility, and controller is provided, e.g., public class ClassImpl { @Getter @Setter String name; @Getter @Setter String[] clazz; } and the loader that reads the file and creates instances.
The article also covers loading plugin JARs at runtime by scanning a directory, creating a URLClassLoader , and invoking methods via reflection, with detailed code snippets for jar discovery, class loading, and method execution, such as public static void loadJarFile(File path) throws Exception { URL url = path.toURI().toURL(); URLClassLoader classLoader = (URLClassLoader) ClassLoader.getSystemClassLoader(); Method method = URLClassLoader.class.getMethod("sendMsg", Map.class); method.setAccessible(true); method.invoke(classLoader, url); } .
Finally, it explains Spring Boot’s own plugin mechanism using spring.factories and SpringFactoriesLoader , demonstrates defining a public interface SmsPlugin { void sendMessage(String message); } interface, two implementations public class BizSmsImpl implements SmsPlugin { @Override public void sendMessage(String message) { System.out.println("this is BizSmsImpl sendMessage..." + message); } } and public class SystemSmsImpl implements SmsPlugin { @Override public void sendMessage(String message) { System.out.println("this is SystemSmsImpl sendMessage..." + message); } } , the factories file content, and a REST endpoint that loads all implementations and calls their sendMessage method.
A complete case study combines the above techniques into a multi‑module project where a central module defines the plugin interface and other modules provide Alibaba Cloud and Tencent Cloud SMS implementations, showing how configuration and ServiceLoader together enable flexible, hot‑pluggable SMS services.
The article concludes that plugin mechanisms are pervasive across languages and frameworks, and mastering them is valuable for modern software architecture.
Architect's Guide
Dedicated to sharing programmer-architect skills—Java backend, system, microservice, and distributed architectures—to help you become a senior architect.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.