Java spi(service provider interface)

什么是spi,下面以mysql驱动为示例,直接贴源码。
1、jdbc在没有采用spi的情况下,是需要我们
Class.forName("com.mysql.jdbc.Driver");

2、看核心类java.sql.DriverManager.

public class DriverManager {

    /* Prevent the DriverManager class from being instantiated. */
    private DriverManager(){}


    /**
     * 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");
    }

private static void loadInitialDrivers() {
        String drivers;
        try {
            drivers = AccessController.doPrivileged(new PrivilegedAction() {
                public String run() {
                    return 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() {
            public Void run() {

                ServiceLoader loadedDrivers = ServiceLoader.load(Driver.class);
                Iterator driversIterator = loadedDrivers.iterator();

                /* Load these drivers, so that they can be instantiated.
                 * It may be the case 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 case a 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's
                 * packaged as service and that service is there in classpath.
                 */
                try{
                    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(":");
        println("number of Drivers:" + driversList.length);
        for (String aDriver : driversList) {
            try {
                println("DriverManager.Initialize: loading " + aDriver);
                Class.forName(aDriver, true,
                        ClassLoader.getSystemClassLoader());
            } catch (Exception ex) {
                println("DriverManager.Initialize: load failed: " + ex);
            }
        }
    }

代码说明:重点看
ServiceLoader loadedDrivers = ServiceLoader.load(Driver.class);
以及

for (String aDriver : driversList) {
            try {
                println("DriverManager.Initialize: loading " + aDriver);
                Class.forName(aDriver, true,
                        ClassLoader.getSystemClassLoader());
            } catch (Exception ex) {
                println("DriverManager.Initialize: load failed: " + ex);
            }
        }

3、看下mysql-connector-java-5.1.46.jar的spi实现


Java spi(service provider interface)_第1张图片
20181118215424.png

META-INF/services/java.sql.Driver的内容是:
com.mysql.jdbc.Driver
com.mysql.fabric.jdbc.FabricMySQLDriver

4、最后贴出Driver实现类的源码:

package com.mysql.jdbc;

import java.sql.DriverManager;
import java.sql.SQLException;

public class Driver extends NonRegisteringDriver implements java.sql.Driver {
         public Driver() throws SQLException {
          }

    static {
        try {
            DriverManager.registerDriver(new Driver());
        } catch (SQLException var1) {
            throw new RuntimeException("Can't register driver!");
        }
    }
}
package com.mysql.fabric.jdbc;

import com.mysql.jdbc.ExceptionInterceptor;
import com.mysql.jdbc.NonRegisteringDriver;
import com.mysql.jdbc.Util;
import java.lang.reflect.Constructor;
import java.sql.Connection;
import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Properties;
import java.util.logging.Logger;

public class FabricMySQLDriver extends NonRegisteringDriver implements Driver {
    public static final String FABRIC_URL_PREFIX = "jdbc:mysql:fabric://";
    public static final String FABRIC_SHARD_KEY_PROPERTY_KEY = "fabricShardKey";
    public static final String FABRIC_SHARD_TABLE_PROPERTY_KEY = "fabricShardTable";
    public static final String FABRIC_SERVER_GROUP_PROPERTY_KEY = "fabricServerGroup";
    public static final String FABRIC_PROTOCOL_PROPERTY_KEY = "fabricProtocol";
    public static final String FABRIC_USERNAME_PROPERTY_KEY = "fabricUsername";
    public static final String FABRIC_PASSWORD_PROPERTY_KEY = "fabricPassword";
    public static final String FABRIC_REPORT_ERRORS_PROPERTY_KEY = "fabricReportErrors";

    public FabricMySQLDriver() throws SQLException {
    }

    public Connection connect(String url, Properties info) throws SQLException {
        Properties parsedProps = this.parseFabricURL(url, info);
        if (parsedProps == null) {
            return null;
        } else {
            parsedProps.setProperty("fabricProtocol", "http");
            if (Util.isJdbc4()) {
                try {
                    Constructor jdbc4proxy = Class.forName("com.mysql.fabric.jdbc.JDBC4FabricMySQLConnectionProxy").getConstructor(Properties.class);
                    return (Connection)Util.handleNewInstance(jdbc4proxy, new Object[]{parsedProps}, (ExceptionInterceptor)null);
                } catch (Exception var5) {
                    throw (SQLException)(new SQLException(var5.getMessage())).initCause(var5);
                }
            } else {
                return new FabricMySQLConnectionProxy(parsedProps);
            }
        }
    }

    public boolean acceptsURL(String url) throws SQLException {
        return this.parseFabricURL(url, (Properties)null) != null;
    }

    Properties parseFabricURL(String url, Properties defaults) throws SQLException {
        return !url.startsWith("jdbc:mysql:fabric://") ? null : super.parseURL(url.replaceAll("fabric:", ""), defaults);
    }

    public Logger getParentLogger() throws SQLException {
        throw new SQLException("no logging");
    }

    static {
        try {
            DriverManager.registerDriver(new FabricMySQLDriver());
        } catch (SQLException var1) {
            throw new RuntimeException("Can't register driver", var1);
        }
    }
}
}

你可能感兴趣的:(Java spi(service provider interface))