Recently learned JDK SPI

What is JDK SPI

Several recent workday SPI colleagues said several times this term, though, and I'm okay, but my heart still silently wish to learn about, or else the next SPI and I said, do not even know what it would be embarrassing.

SPI So what is it? SPI stands for Service Provider Interface, is a more important concept in Java, Java is intended to be provided by a third-party implementation or expansion of API, or in other words, SPI is a service discovery mechanism .

 

JDK SPI instructions and examples

SPI is relatively simple to use, only need to follow the following steps:

  • Jar package created under the META-INF / services directory with a "fully qualified name of the interface" as the name of the file content fully qualified name of the implementation class
  • Interface class is a jar on the classpath
  • The main program module implemented by java.util.ServiceLoader dynamic state, it is found fully qualified name of the implementation class by the configuration file in the scan META-INF / services directory, the class is loaded into the JVM
  • SPI implementation class must bring a constructor with no arguments

Then we look at a specific example, first define a SpiService, it is an interface:

package org.xrq.test.spi;

public interface SpiService {

    public void hello();
    
}

Achieve two categories, namely SpiServiceA and SpiServiceB:

package org.xrq.test.spi;

public class SpiServiceA implements SpiService {

    public void hello() {
        System.out.println("SpiServiceA.Hello");
    }
    
}
package org.xrq.test.spi;

public class SpiServiceB implements SpiService {

    @Override
    public void hello() {
        System.out.println("SpiServiceB.hello");
    }
    
}

Then we built a META-INF / services folder, which built a file, file name is the fully qualified name org.xrq.test.spi.SpiService interface:

Contents of the file is SpiService implementation class SpiServiceA, SpiServiceB fully qualified name:

org.xrq.test.spi.SpiServiceA 
org.xrq.test.spi.SpiServiceB

This is done! Then we write a test class automatically loads at these two categories:

public  class SpiTest { 

    @Test 
    public  void testSpi () { 
        Service Loading <Eating Vice> = ServiceLoader.load service loader (Eating Vice. class ); 
        
        Iterator <Eating Vice> iterator = serviceLoader.iterator ();
        while (iterator.hasNext ()) { 
            Eating Vice eat vice = iterator.next (); 
            
            spiService.hello (); 
        } 
    } 
    
}

Results at a glance, call the hello () method:

SpiServiceA.Hello 
SpiServiceB.hello

This is an example of the use of SPI, and then we look at the practical application of SPI in the scene.

 

Application of SPI in JDBC

Looking back at nearly forty years ago, the article https://www.cnblogs.com/xrq730/p/4851944.html , Entitled " JDBC study 2: Why write Class.forName (" XXX ")?"The article which was really immature technology, why not write Class.forName (" XXX ") interpretation now seems really weak burst, last reply to the first floor of users' reasons do not write that the new version of the JDBC use the SPI ", so learning a bit SPI immediately think of the examples, so you talk about the practical application of the JDBC SPI's.

In older versions of JDBC, assuming we are using MySql, JDBC initialization time is required to explicitly call Class.forName ( " com.mysql.jdbc.Driver ") this one, but you do not need after a version do this step, and as noted above this is achieved by SPI, how to understand it. Class.forName fact no real meaning, in fact, neither new nor reflective objects generated objects, it's just to call the static method block com.mysql.jdbc.Driver of it:

public class Driver extends NonRegisteringDriver implements java.sql.Driver {
    //
    // Register ourselves with the DriverManager
    //
    static {
        try {
            java.sql.DriverManager.registerDriver(new Driver());
        } catch (SQLException E) {
            throw new RuntimeException("Can't register driver!");
        }
    }

    /**
     * Construct a new driver and register it with DriverManager
     * 
     * @throws SQLException
     *             if a database error occurs.
     */
    public Driver() throws SQLException {
        // Required for Class.forName().newInstance()
    }
}

Method of action is only a block by jdk own DriverManager registered Driver, registerDrivers nothing routine method, the Driver into CopyOnArrayList inside it:

public static synchronized void registerDriver(java.sql.Driver driver, DriverAction da)
      throws SQLException {

    /* Register the driver if it has not already been added to our list */
    if(driver != null) {
        registeredDrivers.addIfAbsent(new DriverInfo(driver, da));
    } else {
        // This is for compatibility with the original DriverManager
        throw new NullPointerException();
    }

    println("registerDriver: " + driver);

}

From a JDK version, do not know which specific version, abandoned this operation, the new look of DriverManager, I was JDK1.8 of:

/**
 * Load the initial JDBC drivers by checking the System property
 * jdbc.properties and then use the {@code ServiceLoader} mechanism
 */
static {
    loadInitialDrivers();
    println("JDBC DriverManager initialized");
}

LoadInitialDrivers direct look at the core of this approach:

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;
    }
});

SPI used to find a way to see this java.sql.Driver file from the META-INF / services, and found inside the Driver implementation class by one injection. Finally, we look at the next Iterator () method to do something completely understand, by next () method call:

private S nextService() {
    if (!hasNextService())
        throw new NoSuchElementException();
    String cn = nextName;
    nextName = null;
    Class<?> c = null;
    try {
        c = Class.forName(cn, false, loader);
    } catch (ClassNotFoundException x) {
        fail(service,
             "Provider " + cn + " not found");
   }
    if (!service.isAssignableFrom(c)) {
       fail(service,
             "Provider " + cn  + " not a subtype");
    }
    try {
        S p = service.cast(c.newInstance());
        providers.put(cn, p);
        return p;
    } catch (Throwable x) {
        fail(service,
             "Provider " + cn + " could not be instantiated",
            x);
    }
    throw new Error();          // This cannot happen
}
            

Class.forName see it, though they are Class.forName, but by the way the user manual SPI do the action becomes a framework to do.

 

Understanding of the SPI

After the last talk about my understanding of the SPI, learning how to use SPI, SPI examples of practical applications, a deep understanding of SPI mechanism to work in the future will truly SPI for our use.

First, we can note that the title is JDK SPI, SPI that is not exclusive JDK. Yes, I understand the SPI is actually a general term for pluggable technology, the simplest example is the USB, the USB standard provides manufacturers, manufacturers create their own USB peripherals according to the standard, such as a mouse, keyboard, gamepad etc., but particularly in the USB standard is how to use the computer, the manufacturers do not need the tube.

Back to our code is the same reason. When we develop a framework of time, in addition to ensuring the basic functions, the most important point is what? I think the most important should be loosely coupled, that is, to open for extension, but closed for modification to ensure that the framework for the user to achieve a black box.

Framework can not do all the things that can only be pulled out of the common part of the process, the core is the definition of loose coupling loose enough to achieve a good interface, or can be understood that the extension points, specific extension points allow users to implementation, such different extensions do not need to modify the source code or to customize the framework, which is the benefits of interface-oriented programming.

Back part of our framework for:

  • For JDK is achieved by SPI META-INF / services catalog + ServiceLoader
  • Spring is the way to achieve SPI left N number of interfaces, such as BeanPostProcessor, InitializingBean, DisposableBean, we only need to implement these interfaces can then inject

For existing framework, we can provide the framework for our extension point spread function framework. For write their own framework, SPI remember this thing, stay well enough extension points, which will greatly enhance the scalability you write frame.

 

Guess you like

Origin www.cnblogs.com/xrq730/p/11440174.html
SPI
Recommended