P7 mobile Internet architect Advanced video (daily update) free learning please click:space.bilibili.com/474380680

This article will introduce componentized framework design from an in-depth understanding of the Java SPI mechanism:

I. DEFINITION of SPI mechanism

SPI (Service Provider Interface) is derived from the Service Provider Framework (EffectiveJava). It is a mechanism to decouple service interface from service implementation and greatly improve program scalability. The import service provider is the implementer who introduces the SPI interface and obtains the specific implementation class through local registration discovery, which is easily pluggable.

Two, the typical example: JDBC design

Typically, various vendors (such as Mysql and Oracle) develop their own Driver implementation logic according to a unified specification (java.sql.Driver). Clients using JDBC do not need to change the code and simply introduce different SPI interface services. Mysql is com. Mysql. JDBC. Drive, Oracle is Oracle.. JDBC driver. OracleDriver.


The pseudocode is as follows:

//Class. ForName (driver); //Class. //1.getConnection() to connect to the MySQL database. It is possible to register multiple drivers, which are returned after a successful connection is traversed. con = DriverManager.getConnection(mysqlUrl,user,password); //2. Create a statement object to execute a SQL statement. Statement statement = con.createStatement(); //3.ResultSet class, used to store the obtained ResultSet!! ResultSet rs = statement.executeQuery(sql);Copy the code

JDBC connection source code analysis

  1. The java.SQL.DriverManager static block is initially executed in which the JDBC concrete implementation is loaded using the SPI mechanism
/ / Java SQL. DriverManager. Java / / when the DriverManager. GetConnection (..) GetConnection (..) /** * Load the initial JDBC drivers by checking the System property * jdbc.properties andthen use the {@code ServiceLoader} mechanism
     */
    static {
        loadInitialDrivers();
        println("JDBC DriverManager initialized");
    }
Copy the code

2. LoadInitialDrivers () has completed the search and loading of the introduced database driver. This example only introduces mysql from Oracle manufacturer.

//java.util.serviceLoader.java

   private static void loadInitialDrivers() {
        String drivers;
        try {
            drivers = AccessController.doPrivileged(new PrivilegedAction<String>() {
                public String run() {// Load using system variablesreturn System.getProperty("jdbc.drivers"); }}); } catch (Exception ex) { drivers = null; } // If the Driver is packaged as a Service Provider load it. // Get all the drivers through the classloader // exposed as a java.sql.Driver.class service. // ServiceLoader.load() replaces the sun.misc.Providers() AccessController.doPrivileged(new PrivilegedAction<Void>() { public Voidrun() {// To find a specific provider, find the specific implementation in meta-INF /services/***.driver. ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class); Iterator<Driver> driversIterator = loadedDrivers.iterator(); /* Load these drivers, so that they can be instantiated. * It may be thecase that the driver class may not be there
                 * i.e. there may be a packaged driver with the service class
                 * as implementation of java.sql.Driver but the actual class
                 * may be missing. In that casea java.util.ServiceConfigurationError * will be thrown at runtime by the VM trying to locate * and load the service. * *  Adding a try catch block to catch those runtime errors *if driver not available in classpath but it'// / packaged as service and that service is there in classpath. while(driversIterator.hasNext()) { driversIterator.next(); }} catch(Throwable t) {// Do nothing} return null; }}); println("DriverManager.initialize: jdbc.drivers = " + drivers); if (drivers == null || drivers.equals("")) { return; } String[] driversList = drivers.split(":"); . }}Copy the code

3. Java. Util. ServiceLoader load spi implementation class.

The core code of the previous step is as follows, then we will analyze:

//java.util.serviceLoader.java ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class); Iterator<Driver> driversIterator = loadedDrivers.iterator(); Try {// Find the fully qualified name of the concrete implementation classwhile(driversiterator.hasnext ()) {// Load and initialize the implementation of driversiterator.next (); } } catch(Throwable t) { // Do nothing }Copy the code

This is mainly done with ServiceLoader. Let’s look at the ServiceLoader implementation in order of execution:

Public static <S> ServiceLoader<S> load(class <S> service) {static <S> ServiceLoader<S> load(class <S> service) { ClassLoader cl = Thread.currentThread().getContextClassLoader();return ServiceLoader.load(service, cl);
    }
    public static <S> ServiceLoader<S> load(Class<S> service,
                                            ClassLoader loader)
    {
        return new ServiceLoader<>(service, loader);
    }
Copy the code

Iterate over all existing service implementations

        public boolean hasNext() {
            if (acc == null) {
                return hasNextService();
            } else {
                PrivilegedAction<Boolean> action = new PrivilegedAction<Boolean>() {
                    public Boolean run() { returnhasNextService(); }};returnAccessController.doPrivileged(action, acc); }}Copy the code
Private static final String PREFIX = private static final String PREFIX ="META-INF/services/";

       private boolean hasNextService() {
            if(nextName ! = null) {return true;
            }
            if(configs == null) { try { String fullName = PREFIX + service.getName(); // Read a file in the meta-INF directory of the classpath through a relative path, i.e. read the fully qualified name of the implementation class of the service providerif (loader == null)
                        configs = ClassLoader.getSystemResources(fullName);
                    else
                        configs = loader.getResources(fullName);
                } catch (IOException x) {
                    fail(service, "Error locating configuration files", x); } // Check whether the implementation class fully qualified name is read, such as mysql "com.mysql.jdbc.driver"while((pending == null) || ! pending.hasNext()) {if(! configs.hasMoreElements()) {return false; } pending = parse(service, configs.nextElement()); } nextName = pending.next(); //nextName is saved for subsequent initialization of the implementation classreturn true; // Return if foundtrueNext ()}Copy the code
        public S next() {
            if(acc == null) {// Determine whether the serviceLoader object is initializedreturn nextService();
            } else {
                PrivilegedAction<S> action = new PrivilegedAction<S>() {
                    public S run() { returnnextService(); }};return AccessController.doPrivileged(action, acc);
            }
        }
      private S nextService() {
            if(! hasNextService()) throw new NoSuchElementException(); String cn = nextName; NextName = null; nextName = null; Class<? > c = null; Try {// Loading bytecode returns a class object. // 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 {// Initializes the implementation class. The implementation class is triggered by a static block to register with DriverManager(which combines a CopyOnWriteArrayList registeredDrivers member variable) S p = service.cast(c.newInstance()); providers.put(cn, p); // Local cache (fully qualified name, implementation class object)return p;
            } catch (Throwable x) {
                fail(service,
                     "Provider " + cn + " could not be instantiated",
                     x);
            }
            throw new Error();          // This cannot happen
        }
Copy the code

In the previous step, Sp = service.cast(c.newinstance ()) results in the initialization of a concrete implementer, such as mysqlJDBC, which triggers the following code:

//com.mysql.jdbc.Driver.java ...... private final static CopyOnWriteArrayList<DriverInfo> registeredDrivers = new CopyOnWriteArrayList<>(); . Static {try {/ / concurrent security to a copyOnWriteList China Java SQL. The DriverManager. RegisterDriver (new Driver ()); } catch (SQLException E) { throw new RuntimeException("Can't register driver!"); }}Copy the code

4. The final Driver register and initialize completely, begin to execute DriverManager. GetConnection (url, “root”, “root”) method and return.

Example Four items :spiInterface, spiA, spiB, and spiDemo

A com.zs.IOperation interface is defined in spiInterface.

SpiA and spiB are the implementation classes and service providers of this interface.

As a client, spiDemo introduces spiA or spiB dependencies, interfaces oriented programming, obtains concrete implementers through SPI and executes interface methods.

├ ─ spiA │ └ ─ SRC │ ├ ─ the main │ │ ├ ─ Java │ │ │ └ ─ com │ │ │ └ ─ zs │ │ ├ ─ resources │ │ │ └ ─ meta-inf │ │ │ └ ─ services │ │ └ ─ webapp │ ├ ─ ├ ─ web-inf │ ├ ─test│ └ ─ Java ├ ─ spiB │ └ ─ SRC │ ├ ─ the main │ │ ├ ─ Java │ │ │ └ ─ com │ │ │ └ ─ zs │ │ ├ ─ resources │ │ │ └ ─ meta-inf │ │ │ └ ─ services │ │ ├ ─ ├ ─ sci-imptest│ └ ─ Java ├ ─ spiDemo │ └ ─ SRC │ ├ ─ the main │ │ ├ ─ Java │ │ │ └ ─ com │ │ │ └ ─ zs │ │ ├ ─ resources │ │ └ ─ webapp │ │ └ ─ WEB - INF │ └ ─test│ └ ─ Java └ ─ spiInterface └ ─ SRC ├ ─ the main │ ├ ─ Java │ │ └ ─ com │ │ └ ─ zs │ ├ ─ resources │ └ ─ webapp │ └ ─ WEB - INF └ ─test└ ─ Java └ ─ spiInterfaceCopy the code

spiDemo

package com.zs;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Iterator;
import java.util.ServiceLoader;

public class Launcher {

    public static void main(String[] args) throws Exception {
//      jdbcTest();
        showSpiPlugins();
        
    }
    private static void jdbcTest() throws SQLException {
        String url = "jdbc:mysql://localhost:3306/test";
        Connection conn = DriverManager.getConnection(url, "root"."root");
        Statement statement = conn.createStatement();
        ResultSet set = statement.executeQuery("select * from test.user");
        while (set.next()) {
            System.out.println(set.getLong("id"));
            System.out.println(set.getString("userName"));
            System.out.println(set.getInt("age"));
        }
    }
    private static void showSpiPlugins() {
        ServiceLoader<IOperation> operations = ServiceLoader.load(IOperation.class);
        Iterator<IOperation> operationIterator = operations.iterator();
        
        while(operationIterator.hasNext()) { IOperation operation = operationIterator.next(); System.out.println(operation.operation(6, 3)); }}}Copy the code

SPI sample complete code. The original link blog.csdn.net/lemon89/art… Ali P7 mobile Internet architect advanced video (updated daily) free learning please click: space.bilibili.com/474380680