- Foreword
- ServiceLoader
- The use JDBC
- More usage scenarios
- The principle
- SpringFactoriesLoader
- Epilogue
Foreword
When first contact SPI is looking at "Java core computing volume" in the relevant sections of JDBC, then we see that can be omitted by the high version of the JDBC Class.forName
driver to load this step because of the high version of the JDBC automatically by SPI mechanism load the registration drive.
At that time, when I feel very pleasantly surprised to see, finally I do not have to write long and boring try-catch
a.
Later in the process of reading the source code, he also found that the Spring Java SPI also achieved a similar mechanism of function, some research and found SPI mechanism either in use or in the implementation is very simple.
So, I think you can sum up the entire blog post.
Hide content
The last time a blog or No. 22 June, down more than 100 days, feeling a little Shousheng @ \ _ @ServiceLoader
The full name of SPI (Service Provider Interface), is the built-in JDK provides a service discovery mechanism. It consists of tools java.util.ServiceLoader
to provide appropriate support.
One of the two main roles:
- Service - service, usually as an interface or an abstract class, concrete class although it could be, but is generally not recommended to do so
- Service Provider - service providers, service implementation class
In use, the need to META-INF/services
lower the creation and services were fully qualified the same file, and then write in the document service provider fully qualified name can be used #
as a comment. For example, we can file mysql-connector-java/META-INF/services/java.sql.Driver
found in the following:
com.mysql.jdbc.Driver
com.mysql.fabric.jdbc.FabricMySQLDriver
Then, you can ServiceLoader
acquire these service providers. Since ServiceLoader method does not provide direct access to service providers, therefore, can only be obtained by an iterative way:
ServiceLoader<Service> loader = Service.load(Service.class);
for (Service service : loader) {
// ...
}
You can see, ServiceLoader is very simple to use, more relevant content and ServiceLoader can look at the official document: ServiceLoader (the Java Platform SE 8)
The use JDBC
If you are looking for a used example of SPI mechanism, then, is the most direct way of JDBC by SPI load the driver, and here you can look at the use of JDBC:
public class DriverManager {
static {
loadInitialDrivers();
}
private static void loadInitialDrivers() {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
Iterator<Driver> driversIterator = loadedDrivers.iterator();
try{
while(driversIterator.hasNext()) {
driversIterator.next();
}
} catch(Throwable t) {
// Do nothing
}
return null;
}
});
}
}
Can be found by the code after the simplified above, in the load DriverManager
is called static initialization performed by the code block when the class loadInitialDrivers
methods, and this method will by ServiceLoader
loading all of the Driver
providers.
Driver provided in the respective class, such as based com.mysql.jdbc.Driver
on the existence of the form code:
static {
try {
java.sql.DriverManager.registerDriver(new Driver());
} catch (SQLException E) {
throw new RuntimeException("Can't register driver!");
}
}
Is not it simple? When loading each Driver DriverManager loading mechanism via the SPI, and then in the respective Driver their own static initializer block DriverManager to register itself.
More usage scenarios
Can be found by using the JDBC SPI mechanisms, to use SPI, then it is very simple, so, we can use SPI in place?
SPI mechanism due to the limitations of a single ServiceLoader can only load a single type of Service, but must also create the appropriate files into the META-INF/services
directory, so using the best scenario is similar to JDBC in this, you can access other services provided by a single object those scenes, namely: You can use the facade pattern scene.
For example, now there are many commonly used Java JSON library, such as Gson, FastJSON, Jackson, etc., these libraries can use to meet the needs of a simple majority of the package, then we can consider mechanisms by SPI achieve a facade of these JSON libraries, will be put under treatment JSON service Provider to complete, and we use these services through the facade.
As a result, while we can provide their own default implementation, it can also leave extensible interface, there is no need to manually load to those achieved.
The principle
SPI is not only very simple in use, its implementation principle is very simple, the key in ClassLoader.getResources
this method, SPI loading services is through ClassLoader.getResources
to find a way META-INF/services
corresponding file in the directory, and then parse the file to get the class name of the service provider.
Finally, Class.forName() -> clazz.newInstance()
to get an instance returned.
Very simple and straightforward implementation, more noteworthy is the ClassLoader.getResources
use of a method, for example, you can in one Spring
implementation of projects under the following code:
public class Test {
public static void main(String[] args) throws Exception {
Enumeration<URL> urls = Test.class.getClassLoader().getResources("META-INF/spring.factories");
while (urls.hasMoreElements()) {
System.out.println(urls.nextElement());
}
}
}
This is the Spring by SpringFactoriesLoader
starting point to load related classes.
SpringFactoriesLoader
SpringFactoriesLoader Spring is one of a very important extension mechanism, its use and implementation of the principles and SPI is very similar, but provides more powerful features.
SPI and different, because the SpringFactoriesLoader configuration file format is a properties
file, there is no need to be so creates a file for each service like SPI, but rather choose to direct all services are thrown into the META-INF/spring.factories
file.
For example, spring-boot-autoconfigure in part:
# Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener
# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.autoconfigure.BackgroundPreinitializer
# Auto Configuration Import Listeners
org.springframework.boot.autoconfigure.AutoConfigurationImportListener=\
org.springframework.boot.autoconfigure.condition.ConditionEvaluationReportAutoConfigurationImportListener
# Auto Configuration Import Filters
org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\
org.springframework.boot.autoconfigure.condition.OnClassCondition
# ...
More use can refer to: SpringFactoriesLoader (5.2.0.RELEASE the Spring Framework API)
Epilogue
Overall, both ServiceLoader or SpringFactoriesLoader, their basic principle is the same, is through ClassLoader.getResources
to find the appropriate configuration file method, and then parse the file to get the fully qualified name of the service provider.
With the benefit of powerful Java reflection mechanism, to get the fully qualified name basically can do whatever @ _ @
Hide content
JSON shabby facade: DefaultJsonProviderFactory.java