Java - SPI mechanism

SPI (Service Provider Interface) is a service that provides built-in JDK discovery mechanism.

In normal development we have actually come into contact with, but the general development really do not have access, like jdbc, apache logging mechanism and so used SPI

The SPI interface is provided by the core Java libraries, and the SPI is implementation classes are included into the path (the CLASSPATH) as Java applications depend jar package. For example: JDBC is achieved is dependent in through mysql maven.

So the question is, SPI interface is part of the Java core libraries, by the bootstrap class loader (Bootstrap Classloader) to be loaded. SPI is the realization by the system class loader (System ClassLoader) to load.

Bootstrap class loader when loading is unable to find the SPI implementation class, as specified in the parent delegation model, the bootstrap class loader BootstrapClassloader can not delegate system class loader to load AppClassLoader. At this time, how to solve this problem?

Thread context class loader born, it appears also destroys the parent class loader delegation model, so that the procedure can be inverse class loading.

From the JDBC Case Study:

Connection conn = java.sql.DriverManager.getConnection(url, "name", "password");

Usually in the above database connection, then look DriverManager class

static {
        loadInitialDrivers();
        println("JDBC DriverManager initialized");
    }

There is a static code block DriverManager class. Obviously it calls loadInitialDrivers, then loadInitialDrivers ways of looking at

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

                ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
                Iterator<Driver> 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);
            }
        }
    }

The key message is that this tool ServiceLoader class, it is a tool to provide the service implementation class jdk find: java.util.ServiceLoader. SPI will use the mechanism it uses to achieve service

Driver java.sql.Driver above package is provided by the JDK specification, which is an interface. That other vendors is how to achieve?

mysql maven introduced it will depend jar automatically added to the class path / CLASSPATH under

<dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>6.0.6</version>
        </dependency>

And it provides a mechanism for SPI profile (must be META-INF / services path classpath)

Services provided to achieve just that we introduced mysql packages Driver

Filenames and required interfaces fully qualified class name the same java.sql.Driver

com.mysql.cj.jdbc.Driver need to achieve java.sql.Driver
public class Driver extends NonRegisteringDriver implements java.sql.Driver {}

 

Thread context class loader

In the beginning when the database connection, getConnection () method:

//  Worker method called by the public getConnection() methods.
    private static Connection getConnection(
        String url, java.util.Properties info, Class<?> caller) throws SQLException {
        /*
         * When callerCl is null, we should check the application's
         * (which is invoking this class indirectly)
         * classloader, so that the JDBC driver class outside rt.jar
         * can be loaded from here.
         */
        ClassLoader callerCL = caller != null ? caller.getClassLoader() : null;
        synchronized(DriverManager.class) {
            // synchronize loading of the correct classloader.
            if (callerCL == null) {
                callerCL = Thread.currentThread().getContextClassLoader();
            }
        }

        if(url == null) {
            throw new SQLException("The url cannot be null", "08001");
        }

        println("DriverManager.getConnection(\"" + url + "\")");

        // Walk through the loaded registeredDrivers attempting to make a connection.
        // Remember the first exception that gets raised so we can reraise it.
        SQLException reason = null;

        for(DriverInfo aDriver : registeredDrivers) {
            // If the caller does not have permission to load the driver then
            // skip it.
            if(isDriverAllowed(aDriver.driver, callerCL)) {
                try {
                    println("    trying " + aDriver.driver.getClass().getName());
                    Connection con = aDriver.driver.connect(url, info);
                    if (con != null) {
                        // Success!
                        println("getConnection returning " + aDriver.driver.getClass().getName());
                        return (con);
                    }
                } catch (SQLException ex) {
                    if (reason == null) {
                        reason = ex;
                    }
                }

            } else {
                println("    skipping: " + aDriver.getClass().getName());
            }

        }

        // if we got here nobody could connect.
        if (reason != null)    {
            println("getConnection failed: " + reason);
            throw reason;
        }

        println("getConnection: no suitable driver found for "+ url);
        throw new SQLException("No suitable driver found for "+ url, "08001");
    }

核心点:Thread.currentThread().getContextClassLoader()

 

 

Simple example:

ColorInterface new interface implementation class 2 BlackService, WhiteService were achieved ColorInterface

package com.shentb.hmb.spi;

public interface ColorInterface {
    void sayColor();
}
package com.shentb.hmb.spi.impl;

import com.shentb.hmb.spi.ColorInterface;

public class BlackService implements ColorInterface {
    @Override
    public void sayColor() {
        System.err.println("Black");
    }
}
package com.shentb.hmb.spi.impl;

import com.shentb.hmb.spi.ColorInterface;

public class WhiteService implements ColorInterface {
    @Override
    public void sayColor() {
        System.err.println("White");
    }
}

Under the new project classpath / META-INF / services directory, create a new directory com.shentb.hmb.spi.ColorInterface file, the file name needs and ColorInterface fully qualified class name the same

Content is fully qualified class name of the service implementation.

 

Published 288 original articles · won praise 88 · views 430 000 +

Guess you like

Origin blog.csdn.net/ypp91zr/article/details/91810554