Java SPI and its implementation mechanism

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.forNamedriver 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-catcha.

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.ServiceLoaderto 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/serviceslower 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.Driverfound in the following:

com.mysql.jdbc.Driver
com.mysql.fabric.jdbc.FabricMySQLDriver

Then, you can ServiceLoaderacquire 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 DriverManageris called static initialization performed by the code block when the class loadInitialDriversmethods, and this method will by ServiceLoaderloading all of the Driverproviders.

Driver provided in the respective class, such as based com.mysql.jdbc.Driveron 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/servicesdirectory, 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.getResourcesthis method, SPI loading services is through ClassLoader.getResourcesto find a way META-INF/servicescorresponding 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.getResourcesuse of a method, for example, you can in one Springimplementation 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 SpringFactoriesLoaderstarting 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 propertiesfile, 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.factoriesfile.

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.getResourcesto 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

Guess you like

Origin www.cnblogs.com/rgbit/p/11627434.html