👉 This may be useful for you community
🐱 One-on-one communication/interview guide/resume optimization/job hunting advice, welcome to join「Yudao Rapid Development Platform」Knowledge Planet. Below are some materials provided by the planet:
《Project Practice (Video)》: Learn from the book, practice in reality 《High-Frequency Interview Questions in Internet》: Facing the resume, learning, and blooming 《Architecture x System Design》: Mastering high-frequency interview scenarios 《Java Learning Guide》: Systematic learning, mainstream technology stack in the Internet 《Must-Read Java Source Code Column》: Knowing the why and how
👉This may be a useful open-source project for you
A domestic open-source project with over 100,000 stars, the front end includes management background + WeChat mini program, and the back end supports monolithic and microservice architectures.
Features include RBAC permissions, SaaS multi-tenancy, data permissions, e-commerce, payment, workflow, large screen reports, WeChat official account, CRM, and more:
Boot Repository: https://gitee.com/zhijiantianya/ruoyi-vue-pro Cloud Repository: https://gitee.com/zhijiantianya/yudao-cloud Video Tutorial: https://doc.iocoder.cn 【First batch in China】Supports JDK 21 + SpringBoot 3.2.2, JDK 8 + Spring Boot 2.7.18 dual versions
Source: juejin.cn/post/7395433541482823715
-
What is Java’s SPI -
Difference between SPI and API -
Implementation Process
What is Java’s SPI
Java SPI (Service Provider Interface) is a service provider interface, a service discovery and loading mechanism provided by Java that allows developers to define multiple implementations for an interface and dynamically discover and load these implementations at runtime.
The core of the Java SPI mechanism is that it provides a way for service providers to provide specific implementation classes for a certain interface according to the SPI convention. These implementation classes are placed in a specific location, such as the META-INF/services
directory, and specified through configuration files. When these services are needed, the Java runtime environment can automatically scan these directories, find and load the corresponding implementation classes, thus achieving dynamic discovery and loading of services.
The main uses of Java SPI include:
-
Service providers can provide extension points for frameworks or libraries without modifying business code. -
Allow dynamic insertion or replacement of component implementations at runtime, encouraging loosely coupled design principles. -
Allow third-party extensions and replacements of components in core libraries, enriching the Java ecosystem and providing great flexibility for developers.
In Java, SPI is widely used in various frameworks and libraries for extension, such as Servlet container initialization, type conversion, logging, etc. Through the SPI mechanism, Java applications can easily integrate and use services provided by third parties without modifying business code, thus improving the scalability and maintainability of software.
Based on Spring Boot + MyBatis Plus + Vue & Element, this is a backend management system + user mini program that supports RBAC dynamic permissions, multi-tenancy, data permissions, workflows, third-party login, payment, SMS, e-commerce, etc.
Project Address: https://github.com/YunaiV/ruoyi-vue-pro Video Tutorial: https://doc.iocoder.cn/video/
Difference between SPI and API
The main differences between SPI and API lie in their definition methods, calling methods, flexibility, dependency relationships, and purposes.
-
Definition Method: API is actively written and made public by developers for other developers to use, while SPI is defined by the framework or library provider for third-party developers to implement. -
Calling Method: API uses direct method calls to use functionality, while SPI specifies the concrete implementation classes through configuration files, which are then automatically loaded and called by the framework or library. -
Flexibility: The implementation classes of API must be determined at compile time and cannot be dynamically replaced; while SPI implementation classes can be dynamically loaded and replaced at runtime based on the contents of the configuration files. -
Dependency Relationship: API is depended upon by the caller, meaning the application needs to include the library containing the API to use its functionality; while SPI is depended upon by the callee, meaning the framework or library needs to include the library of the third-party implementation class to load and call it. -
Purpose: API is usually used to describe programming interfaces provided externally by libraries, frameworks, operating systems, services, etc., and developers call the corresponding functionality through APIs to implement their applications. SPI defines a plugin-based architecture that allows developers to define interfaces and provide different implementations through service providers, with the main purpose of allowing systems to discover and load specific service providers at runtime, thus achieving the ability to dynamically extend and replace functionality.
In summary, API is a specification describing how to interact with a component, while SPI is a mechanism for dynamically discovering and loading components that implement a specific interface.
Based on Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element, this is a backend management system + user mini program that supports RBAC dynamic permissions, multi-tenancy, data permissions, workflows, third-party login, payment, SMS, e-commerce, etc.
Project Address: https://github.com/YunaiV/yudao-cloud Video Tutorial: https://doc.iocoder.cn/video/
Implementation Process
0. Directory Structure
sa-auth Parent Project
-- sa-auth-bus Business Project
-- sa-auth-plugin Project defining SPI interface
-- sa-auth-plugin-ldap Simulated third-party library implementation project
1. Create a project named sa-auth in IDEA, the pom is as follows
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.16.RELEASE</version>
</parent>
<groupId>com.vijay</groupId>
<artifactId>cs-auth</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>cs-auth</name>
<packaging>pom</packaging>
<description>cs-auth</description>
<properties>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.compilerVersion>1.8</maven.compiler.compilerVersion>
</properties>
<modules>
<module>cs-auth-plugin</module>
<module>cs-auth-bus</module>
<module>cs-auth-plugin-ldap</module>
</modules>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
</dependency>
<dependency>
<groupId>com.vijay</groupId>
<artifactId>cs-auth-plugin</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
</dependencies>
</dependencyManagement>
</project>
2. Then create sa-auth-plugin, the pom is as follows
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.vijay</groupId>
<artifactId>cs-auth</artifactId>
<version>0.0.1-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>cs-auth-plugin</artifactId>
<name>cs-auth-plugin</name>
<description>cs-auth-plugin</description>
</project>
3. For sa-auth-bus, the pom is as follows
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.vijay</groupId>
<artifactId>cs-auth</artifactId>
<version>0.0.1-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>cs-auth-bus</artifactId>
<name>cs-auth-bus</name>
<description>cs-auth-bus</description>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.vijay</groupId>
<artifactId>cs-auth-plugin</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.1.1.RELEASE</version>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
4. For sa-auth-plugin-ldap, the pom is as follows
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.vijay</groupId>
<artifactId>cs-auth</artifactId>
<version>0.0.1-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>cs-auth-plugin-ldap</artifactId>
<name>cs-auth-plugin</name>
<description>cs-auth-plugin-ldap</description>
<dependencies>
<dependency>
<groupId>com.vijay</groupId>
<artifactId>cs-auth-plugin</artifactId>
</dependency>
</dependencies>
</project>
5. The created project structure is as follows
6. Open sa-auth-plugin, define the SPI interface
package com.vijay.csauthplugin.service;
/**
* Plugin SPI interface
*
* @author vijay
*/
public interface AuthPluginService {
/**
* Login authentication
*
* @param userName Username
* @param password Password
* @return Authentication result
*/
boolean login(String userName, String password);
/**
* AuthPluginService Name which for conveniently find AuthPluginService instance.
*
* @return AuthServiceName mark a AuthPluginService instance.
*/
String getAuthServiceName();
}
7. Implement the SPI interface in cs-auth-plugin-ldap and package it as a jar, simulating the external provided plugin jar
1. Implement the introduced cs-auth-plug package’s SPI interface package com.vijay.csauthplugin.ldap;
package com.vijay.csauthplugin.ldap;
import com.vijay.csauthplugin.service.AuthPluginService;
/**
* @author vijay
*/
public class LdapProviderImpl implements AuthPluginService {
@Override
public boolean login(String userName, String password) {
return "vijay".equals(userName) && "123456".equals(password);
}
@Override
public String getAuthServiceName() {
return "LdapProvider";
}
}
2. Create a directory META-INF/services
under the resources directory, and create a file with the fully qualified name of the SPI interface class com.vijay.csauthplugin.service.AuthPluginService
in it, writing the fully qualified name of the implementation class LdapProviderImpl
com.vijay.csauthplugin.ldap.LdapProviderImpl
3. Package cs-auth-plugin-ldap as a jar
8. Open cs-auth-plugin-bus
1. Create a plugin package under the project and add a default implementation of the plugin DefaultProviderImpl
package com.vijay.bus.plugin;
import com.vijay.csauthplugin.service.AuthPluginService;
/**
* Default plugin implementation
*
* @author vijay
*/
public class DefaultProviderImpl implements AuthPluginService {
@Override
public boolean login(String userName, String password) {
return "vijay".equals(userName) && "123456".equals(password);
}
@Override
public String getAuthServiceName() {
return "DefaultProvider";
}
}
2. Create a directory META-INF/services
under resources and create a file with the fully qualified name of the SPI interface class com.vijay.csauthplugin.service.AuthPluginService
, the file content should be the fully qualified name of DefaultProviderImpl
com.vijay.bus.plugin.DefaultProviderImpl
3. Custom class loader
package com.vijay.bus.plugin;
import java.net.URL;
import java.net.URLClassLoader;
/**
* Custom class loader
*
* @author vijay
*/
public class PluginClassLoader extends URLClassLoader {
public PluginClassLoader(URL[] urls) {
super(urls);
}
/**
* @param url Path
*/
public void addzURL(URL url) {
super.addURL(url);
}
}
4. Define a class for loading external jar packages
package com.vijay.bus.plugin;
import java.io.File;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
/**
* Load specified directory jar packages
* @author vijay
*/
public class ExternalJarLoader {
/**
* Load external jar packages
*
* @param externalDirPath Jar package directory
*/
public static void loadExternalJars(String externalDirPath) {
File dir = new File(externalDirPath);
if (!dir.exists() || !dir.isDirectory()) {
throw new IllegalArgumentException("Invalid directory path");
}
List<URL> urls = new ArrayList<>();
File[] listFiles = dir.listFiles();
if (Objects.nonNull(listFiles) && listFiles.length > 0) {
ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
try {
for (File file : listFiles) {
if (file.getName().endsWith(".jar")) {
urls.add(file.toURI().toURL());
}
}
PluginClassLoader customClassLoader = new PluginClassLoader(urls.toArray(new URL[0]));
Thread.currentThread().setContextClassLoader(customClassLoader);
} catch (Exception e) {
e.printStackTrace();
Thread.currentThread().setContextClassLoader(contextClassLoader);
}
}
}
}
5. Add the class loader in the startup class
package com.vijay.bus;
import com.vijay.bus.plugin.ExternalJarLoader;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* @author vijay
*/
@SpringBootApplication
public class CsAuthBusApplication {
public static void main(String[] args) {
String jarPath="/Users/vijay/Downloads/build/plugin";
ExternalJarLoader.loadExternalJars(jarPath);
SpringApplication.run(CsAuthBusApplication.class, args);
}
}
6. Create a plugin provider class PluginProvider
to provide implementation classes for Spring Boot injection
package com.vijay.bus.plugin;
import com.vijay.csauthplugin.service.AuthPluginService;
import java.util.ServiceLoader;
/**
* Plugin provider
*
* @author vijay
*/
public class PluginProvider {
/**
* Provides a plugin for injection (default returns the plugin from the external directory, returns the default plugin when there are no plugins in the external directory)
*
* @return Specific plugin implementation
*/
public static AuthPluginService getAuthPluginService() {
ServiceLoader<AuthPluginService> defaultLoad = ServiceLoader.load(AuthPluginService.class);
AuthPluginService plugin = null;
for (AuthPluginService authPluginService : defaultLoad) {
if (authPluginService instanceof DefaultProviderImpl) {
plugin = authPluginService;
} else {
return authPluginService;
}
}
return plugin;
}
}
7. Create a conf
package under the project to inject implementation classes into Spring Boot
package com.vijay.bus.conf;
import com.vijay.bus.plugin.PluginProvider;
import com.vijay.csauthplugin.service.AuthPluginService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author vijay
*/
@Configuration
public class PluginConfig {
@Bean
public AuthPluginService authPluginService() {
return PluginProvider.getAuthPluginService();
}
}
8. Create a controller package under the project, define controller interfaces, and call for testing
package com.vijay.bus.controller;
import com.vijay.csauthplugin.service.AuthPluginService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.HashMap;
import java.util.Map;
/**
* @author vijay
*/
@RestController
public class TestController {
@Resource
private AuthPluginService authPluginService;
@GetMapping("test")
public Object test() {
return new HashMap() {{
put("name", authPluginService.getAuthServiceName());
put("login", authPluginService.login("vijay", "123456"));
}};
}
}
Complete structure
9. Request the interface and test the implementation
At this point, the return is the default implementation. Place the simulated third-party package from the cs-auth-plugin-ldap project into the external jar loading directory, restart the project, and make a request.
The implementation is now the simulated jar implementation.
Welcome to join my knowledge planet to comprehensively enhance technical capabilities.
👉 Joining method,“Long press” or “Scan” the QR code below:
The content of the planet includes: project practice, interview recruitment, source code analysis, learning routes.





If the article is helpful, please read and share.
Thank you for your support (*^__^*)