Click to follow the public account, Java Dry GoodsDelivered promptly👇
Source:juejin.cn/post/7132742686099898398
-
Java SPI Implementation -
Example Explanation -
Create Dynamic Interface -
Implementation Class 1 -
Implementation Class 2 -
Related Tests -
Run Results: -
Source Code Analysis -
Spring SPI -
Spring Example -
Define Interface -
Related Implementations -
Related Test Classes -
Output Results -
Source Code Analysis
SPI (Service Provider Interface) is a built-in service discovery mechanism in JDK, which can be used to enable framework extensions and replace components, mainly used in framework development. For example, Dubbo, Spring, Common-Logging, JDBC, etc., adopt the SPI mechanism, providing different implementations for the same interface to different users, thereby enhancing the framework’s extensibility.
Java SPI Implementation
Java’s built-in SPI resolves the classPath and jar package files in the META-INF/services/ directory named after the fully qualified name of the interface through the java.util.ServiceLoader class and loads the specified interface implementation classes from that file to complete the call.
Example Explanation
Create Dynamic Interface
public interface VedioSPI
{
void call();
}
Implementation Class 1
public class Mp3Vedio implements VedioSPI
{
@Override
public void call()
{
System.out.println("this is mp3 call");
}
}
Implementation Class 2
public class Mp4Vedio implements VedioSPI
{
@Override
public void call()
{
System.out.println("this is mp4 call");
}
}
Create a META-INF/services/ directory under the project’s source directory, and create a file named com.skywares.fw.juc.spi.VedioSPI.
Related Tests
public class VedioSPITest
{
public static void main(String[] args)
{
ServiceLoader<VedioSPI> serviceLoader =ServiceLoader.load(VedioSPI.class);
serviceLoader.forEach(t->{
t.call();
});
}
}
Note: The Java SPI implementation uses ServiceLoader to find the service provider.
Run Results:
Source Code Analysis
The above is just a simple example to implement Java’s built-in SPI functionality. The implementation principle is that ServiceLoader is a built-in tool class in Java for finding service provider interfaces. It finds service provider interfaces by calling the load() method and finally iterates to access the implementation classes of the service provider interfaces one by one.
From the source code, we can find:
-
The ServiceLoader class itself implements the Iterable interface and implements the iterator method, which calls the method in the LazyIterator inner class to create instances of the iterator. -
All service provider interface corresponding files are placed in the META-INF/services/ directory, and the final type determines that the PREFIX directory cannot be changed.
Although the SPI mechanism provided by Java has a very good idea, there are also corresponding drawbacks. Specifically as follows:
-
The built-in method in Java can only be obtained through iteration. -
The service provider interface must be placed in the META-INF/services/ directory.
In response to the problems of Java’s SPI, Spring’s SPI mechanism follows the idea of SPI but expands and optimizes it.
Spring SPI
Spring SPI follows the design idea of Java SPI, and Spring uses the spring.factories method to implement the SPI mechanism, allowing for the extensibility of the Spring framework without modifying the Spring source code.
Spring Example
Define Interface
public interface DataBaseSPI
{
void getConnection();
}
Related Implementations
##DB2 Implementation
public class DB2DataBase implements DataBaseSPI
{
@Override
public void getConnection()
{
System.out.println("this database is db2");
}
}
##Mysql Implementation
public class MysqlDataBase implements DataBaseSPI
{
@Override
public void getConnection()
{
System.out.println("this is mysql database");
}
}
1. Create a new spring.factories file in the project’s META-INF directory.
2. Fill in the relevant interface information, as follows:
com.skywares.fw.juc.springspi.DataBaseSPI = com.skywares.fw.juc.springspi.DB2DataBase, com.skywares.fw.juc.springspi.MysqlDataBase
Note that multiple implementations are separated by commas.
Related Test Classes
public class SpringSPITest
{
public static void main(String[] args)
{
List<DataBaseSPI> dataBaseSPIs =SpringFactoriesLoader.loadFactories(DataBaseSPI.class,
Thread.currentThread().getContextClassLoader());
for(DataBaseSPI datBaseSPI:dataBaseSPIs){
datBaseSPI.getConnection();
}
}
}
Output Results
From the example, we can see that Spring’s implementation of SPI using spring.factories is very similar to Java’s implementation of SPI, but Spring’s SPI method has made relevant optimizations to Java’s SPI, specifically as follows:
-
Java SPI corresponds to a configuration file for each service provider interface, where the configuration file stores all implementation classes for the current interface. Multiple service provider interfaces correspond to multiple configuration files, all configurations are in the services directory; -
Spring factories SPI is a spring.factories configuration file that stores multiple interfaces and their corresponding implementation classes, using the fully qualified name of the interface as the key and the implementation classes as the value to configure, with multiple implementation classes separated by commas, and only one spring.factories configuration file.
So how does Spring achieve SPI by loading spring.factories? We can further analyze through the source code.
Source Code Analysis
Note: loadFactoryNames parses the fully qualified names of the implementation classes specified in the spring.factories file for the interface, and the specific implementation is as follows:
Note: Obtaining all jar package META-INF/spring.factories file paths and returning them as an enumeration. Iterating through the spring.factories file paths, loading and parsing them one by one, integrating the names of the implementation classes of type factoryClass, and after obtaining the fully qualified names of the implementation classes, performing class instantiation operations. The relevant source code is as follows:
Note: Instantiation is achieved through reflection for the corresponding initialization.