Java source code of SPI mechanism

1 Introduction

This is the source notes] [JDK source of interpretation of the first article, this article, we explore related to Java source code of SPI mechanisms.

2 What is the mechanism for SPI

So, what is SPI mechanism?

Service Provider Interface SPI is short, that the service provider interface meaning.

According to the literal meaning we may be a little confused, SPI that white is an extension mechanism, we defined the class that implements an interface in the appropriate configuration file, and then load the instance of the class based on this interface to the configuration file and instantiate In fact SPI is such a thing.

Speaking SPI mechanism, our most common is Java-SPI mechanisms, in addition, SPI mechanism Dubbo and SpringBoot custom.

With SPI mechanism, then it offers the possibility for a number of flexible and extensible framework, rather than some of the framework implementation class will write the code inside died.

So, some framework is how to use the mechanism to be flexible SPI extend it? Here are a few chestnuts to elaborate at:

  1. JDBC driver loading case : the use of Java SPI mechanism, we can introduce different JDBC driver package based on a different database vendors;

  2. SpringBoot of SPI mechanism : we can spring.factoriesadd class to automatically configure our custom, event listeners or initializer like;

  3. SPI mechanism of Dubbo : Dubbo SPI mechanism has even applied thoroughly , Dubbo basically their point of each function provides extension points, such as providing clustering extensions, routing and load balancing expansion expansion expansion almost close to 30 points. If one of the built in Dubbo implementation does not meet our needs, so we only use its SPI mechanism will achieve our implementation can replace Dubbo.

The above three chestnuts let us feel at some intuitive framework for the use of SPI mechanism is how to be flexible extensions.

3 How to use Java in SPI?

Let's look at how to use Java comes with SPI. First a definition of Developerthe interface

// Developer.java

package com.ymbj.spi;

publicinterface Developer {
    void sayHi();
}

Redefinition two Developertwo interface implementation class:

// JavaDeveloper.java

package com.ymbj.spi;

publicclass JavaDeveloper implements Developer {

    @Override
    public void sayHi() {
        System.out.println("Hi, I am a Java Developer.");
    }
}
// PythonDeveloper.java

package com.ymbj.spi;

publicclass PythonDeveloper implements Developer {

    @Override
    public void sayHi() {
        System.out.println("Hi, I am a Python Developer.");
    }
}

Then the project resourcesa new directory under a META-INF/servicesfolder, and then to create a Developerfully qualified name of the interface named file, the file contents:

// com.ymbj.spi.Developer文件

com.ymbj.spi.JavaDeveloper
com.ymbj.spi.PythonDeveloper

Finally, we then create a test class JdkSPITest:

// JdkSPITest.java

publicclass JdkSPITest {

    @Test
    public void testSayHi() throws Exception {
        ServiceLoader<Developer> serviceLoader = ServiceLoader.load(Developer.class);
        serviceLoader.forEach(Developer::sayHi);
    }
}

Running above the test class to run a successful outcome following screenshot:

By the above simple mechanism SPI Demo we know how to use Java to achieve the expansion point loading, recommend the following article: JAVA Supplements - About SPI mechanism, through this article, I believe we will have a Java-SPI deeper understanding, in particular JDBC driver to load this regard.

4 Java source code analysis of the mechanism of SPI

Through the front extension Developersimple interface Demo, we see the Java SPI mechanisms to achieve with ServiceLoaderthis class about, then we first look at the ServiceLoaderclass structure of the code:

// ServiceLoader实现了【Iterable】接口
publicfinalclass ServiceLoader<S>
    implements Iterable<S>{
    privatestaticfinal String PREFIX = "META-INF/services/";
    // The class or interface representing the service being loaded
    privatefinal Class<S> service;
    // The class loader used to locate, load, and instantiate providers
    privatefinal ClassLoader loader;
    // The access control context taken when the ServiceLoader is created
    privatefinal AccessControlContext acc;
    // Cached providers, in instantiation order
    private LinkedHashMap<String,S> providers = new LinkedHashMap<>();
    // The current lazy-lookup iterator
    private LazyIterator lookupIterator;
    // 构造方法
    private ServiceLoader(Class<S> svc, ClassLoader cl) {
        service = Objects.requireNonNull(svc, "Service interface cannot be null");
        loader = (cl == null) ? ClassLoader.getSystemClassLoader() : cl;
        acc = (System.getSecurityManager() != null) ? AccessController.getContext() : null;
        reload();
    }
	
    // ...暂时省略相关代码
    
    // ServiceLoader的内部类LazyIterator,实现了【Iterator】接口
    // Private inner class implementing fully-lazy provider lookup
    privateclass LazyIterator
        implements Iterator<S>{
        Class<S> service;
        ClassLoader loader;
        Enumeration<URL> configs = null;
        Iterator<String> pending = null;
        String nextName = null;

        private LazyIterator(Class<S> service, ClassLoader loader) {
            this.service = service;
            this.loader = loader;
        }
        // 覆写Iterator接口的hasNext方法
        public boolean hasNext() {
            // ...暂时省略相关代码
        }
        // 覆写Iterator接口的next方法
        public S next() {
            // ...暂时省略相关代码
        }
        // 覆写Iterator接口的remove方法
        public void remove() {
            // ...暂时省略相关代码
        }

    }

    // 覆写Iterable接口的iterator方法,返回一个迭代器
    public Iterator<S> iterator() {
        // ...暂时省略相关代码
    }

    // ...暂时省略相关代码

}

It can be seen ServiceLoaderimplements Iterablethe interface, which override iteratormethod can produce an iterator; the same time ServiceLoaderthere is an internal class LazyIterator, but LazyIteratoralso to achieve the Iteratorinterface description LazyIteratoris an iterator.

4.1 ServiceLoader.load way to make early preparations to load the service provider implementation class

Then the source SPI mechanisms we now begin to explore Java, the first look at JdkSPITestthe first sentence of the code ServiceLoader<Developer> serviceLoader = ServiceLoader.load(Developer.class);in the ServiceLoader.load(Developer.class)source code:

// ServiceLoader.java

publicstatic <S> ServiceLoader<S> load(Class<S> service) {
    //获取当前线程上下文类加载器
    ClassLoader cl = Thread.currentThread().getContextClassLoader();
    // 将service接口类和线程上下文类加载器作为参数传入,继续调用load方法
    return ServiceLoader.load(service, cl);
}

Let us look at the ServiceLoader.load(service, cl);methods:

// ServiceLoader.java

publicstatic <S> ServiceLoader<S> load(Class<S> service,
                                        ClassLoader loader)
{
    // 将service接口类和线程上下文类加载器作为构造参数,新建了一个ServiceLoader对象
    return new ServiceLoader<>(service, loader);
}

Continue to look at new ServiceLoader<>(service, loader);is how it was built?

// ServiceLoader.java

private ServiceLoader(Class<S> svc, ClassLoader cl) {
    service = Objects.requireNonNull(svc, "Service interface cannot be null");
    loader = (cl == null) ? ClassLoader.getSystemClassLoader() : cl;
    acc = (System.getSecurityManager() != null) ? AccessController.getContext() : null;
    reload();
}

It can be seen in the construction of ServiceLoaderan object property assignment in addition to its members, but also calls the reloadmethod:

// ServiceLoader.java

public void reload() {
    providers.clear();
    lookupIterator = new LazyIterator(service, loader);
}

It can be seen in the reloadapproach and created a new LazyIteratorobject, and then assigned to lookupIterator.

// ServiceLoader$LazyIterator.java

private LazyIterator(Class<S> service, ClassLoader loader) {
    this.service = service;
    this.loader = loader;
}

Can be seen in the construction of LazyIteratoran object, only to its member variables serviceand loaderattribute assignment Yeah, we source all the way down now, I did not see to META-INF/servicesthe folder to load Developerthe interface implementation class! This is strange, we are ServiceLoaderthe loadmethod name fool you.

Remember when a new analysis of previous code LazyIteratorobject? LazyAs the name suggests is a lazy meaning Iteratoris the meaning of iterations. We guess that at this time LazyIteratorthe role of the object is when iteration should go to load Developerclass that implements the interface.

4.2 ServiceLoader.iterator method, implement lazy loading service provider implementation class

We now look at JdkSPITestthe second sentence of the code serviceLoader.forEach(Developer::sayHi);, after the final implementation of this code will call serviceLoaderthe iteratormethod:

// serviceLoader.java

public Iterator<S> iterator() {
    returnnew Iterator<S>() {

        Iterator<Map.Entry<String,S>> knownProviders
            = providers.entrySet().iterator();

        public boolean hasNext() {
            if (knownProviders.hasNext())
                returntrue;
            // 调用lookupIterator即LazyIterator的hasNext方法
            // 可以看到是委托给LazyIterator的hasNext方法来实现
            return lookupIterator.hasNext();
        }

        public S next() {
            if (knownProviders.hasNext())
                return knownProviders.next().getValue();
            // 调用lookupIterator即LazyIterator的next方法
            // 可以看到是委托给LazyIterator的next方法来实现
            return lookupIterator.next();
        }

        public void remove() {
            thrownew UnsupportedOperationException();
        }

    };
}

It can be seen calling serviceLoaderthe iteratormethod returns an anonymous iterator object, and this anonymous iterator object is actually the equivalent of a facade class, which override hasNextand the nextmethods they were commissioned LazyIteratorof hasNextand nextmethods to achieve.

We continue to debug, found next will enter LazyIteratorthe hasNextmethod:

// serviceLoader$LazyIterator.java

public boolean hasNext() {
    if (acc == null) {
        // 调用hasNextService方法
        return hasNextService();
    } else {
        PrivilegedAction<Boolean> action = new PrivilegedAction<Boolean>() {
            public Boolean run() { return hasNextService(); }
        };
        return AccessController.doPrivileged(action, acc);
    }
}

Continue to follow up hasNextServicemethod:

// serviceLoader$LazyIterator.java

private boolean hasNextService() {
    if (nextName != null) {
        returntrue;
    }
    if (configs == null) {
        try {
            // PREFIX = "META-INF/services/"
            // service.getName()即接口的全限定名
            // 还记得前面的代码构建LazyIterator对象时已经给其成员属性service赋值吗
            String fullName = PREFIX + service.getName();
            // 加载META-INF/services/目录下的接口文件中的服务提供者类
            if (loader == null)
                configs = ClassLoader.getSystemResources(fullName);
            else
                // 还记得前面的代码构建LazyIterator对象时已经给其成员属性loader赋值吗
                configs = loader.getResources(fullName);
        } catch (IOException x) {
            fail(service, "Error locating configuration files", x);
        }
    }
    while ((pending == null) || !pending.hasNext()) {
        if (!configs.hasMoreElements()) {
            returnfalse;
        }
        // 返回META-INF/services/目录下的接口文件中的服务提供者类并赋值给pending属性
        pending = parse(service, configs.nextElement());
    }
    // 然后取出一个全限定名赋值给LazyIterator的成员变量nextName
    nextName = pending.next();
    returntrue;
}

It can be seen in the implementation LazyIteratorof hasNextServicethe time the method will eventually go to META-INF/services/the directory load the contents of the interfaces file that is loaded service provider implements the fully qualified name of the class, and then remove the fully qualified name of a service provider implementation class assigned to the LazyIteratormember variable nextName.

Here, and we understand the LazyIteratorrole is really lazy loading, when used to load will really service provider implementation class.

Thoughts : Why use lazy load it here? Lazy loading of thinking what is it? Lazy loading so what good is it? You can also cited other cases of lazy loaded it?

Similarly, the implementation of End LazyIteratorof hasNextthe process will proceed to LazyIteratorthe nextmethod:

// serviceLoader$LazyIterator.java

public S next() {
    if (acc == null) {
        // 调用nextService方法
        return nextService();
    } else {
        PrivilegedAction<S> action = new PrivilegedAction<S>() {
            public S run() { return nextService(); }
        };
        return AccessController.doPrivileged(action, acc);
    }
}

We continue to follow up nextServicemethod:

// serviceLoader$LazyIterator.java

private S nextService() {
    if (!hasNextService())
        thrownew NoSuchElementException();
    // 还记得在hasNextService方法中为nextName赋值过服务提供者实现类的全限定名吗
    String cn = nextName;
    nextName = null;
    Class<?> c = null;
    try {
        // 【1】去classpath中根据传入的类加载器和服务提供者实现类的全限定名去加载服务提供者实现类
        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 {
        // 【2】实例化刚才加载的服务提供者实现类,并进行转换
        S p = service.cast(c.newInstance());
        // 【3】最终将实例化后的服务提供者实现类放进providers集合
        providers.put(cn, p);
        return p;
    } catch (Throwable x) {
        fail(service,
             "Provider " + cn + " could not be instantiated",
             x);
    }
    thrownew Error();          // This cannot happen
}

See LazyIteratorthe nextServicemethod of example implementation class will eventually service provider prior to loading, and into providersthe collection, and then calls the method (referred to herein as a service provider, such as the implementation class of JavaDeveloperthe sayHimethod).

Note that this is a loaded service provider implementation class, if mainthere is a method to call the service provider implementation class of functions, it will immediately call methods; then continues at instantiate a service provider class.

Thus we see the ServiceLoader.iteratormethod really took to load and instantiate a META-INF/services/service provider interface files in the directory defined in the implementation class.

Design pattern : see, Java's implementation code mechanism SPI applied iterative mode, Iterator shield structural differences inside the various storage objects, providing a unified view of the object to traverse the respective storage (storage object may be set , arrays, etc.). java.util.IteratorAlso iterator pattern achieved: while Java each collection classes generally implements Iterablethe interface, implements its iteratormethods to obtain a Iteratorclass that implements interface object (typically as a set of inner classes), then use Iteratorthe implementation class of the object hasNextand nextmethod to traverse the collection element.

5 JDBC driver to load source code interpretation

SPI previous analysis of the source code of Java implementation mechanism, now the actual case SPI mechanism we will look at Java applications.

As we all know, JDBC driver loading is a typical Java application case of SPI mechanisms. JDBC provides a major interface specification, and this specification in the java api core library ( rt.jarrealization), whereas a different database vendors as long to write this driver code in line with JDBC interface specification, it can be used to connect the Java language the database.

java core library ( rt.jar) to load the JDBC driver with the core interfaces and classes are java.sql.Driverinterfaces and java.sql.DriverManagerclasses, which java.sql.Driverare each database vendor's driver class to implement an interface, and DriverManageris used to manage the database driver class, it is noteworthy that DriverManagerthis class has a registeredDriversset of attributes, for driving the Mysql class stores.

// DriverManager.java

// List of registered JDBC drivers
privatefinalstatic CopyOnWriteArrayList<DriverInfo> registeredDrivers = new CopyOnWriteArrayList<>();

Here to analyze the source code to load the JDBC driver loaded Mysql driver for example.

Introducing our project mysql-connector-javadependency (version is here 5.1.47after), the drive Mysql implementation class file as shown below:

Mysql driver package can see there are two Driverdriving classes, respectively com.mysql.jdbc.Driver, and com.mysql.fabric.jdbc.FabricMySQLDriver, by default, we generally use only the former.

The SPI 5.1 using Java class loading drive Mysql

So then we have to explore how the code under JDBC driver loading is achieved.

First look at a simple JDBC test code:

// JdbcTest.java

publicclass JdbcTest {
    public static void main(String[] args) {
        Connection connection = null;
        Statement statement = null;
        ResultSet rs = null;

        try {
            // 注意:在JDBC 4.0规范中,这里可以不用再像以前那样编写显式加载数据库的代码了
            // Class.forName("com.mysql.jdbc.Driver");
            // 获取数据库连接,注意【这里将会加载mysql的驱动包】
            /***************【主线,切入点】****************/
            connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbc", "root", "123456");
            // 创建Statement语句
            statement = connection.createStatement();
            // 执行查询语句
            rs = statement.executeQuery("select * from user");
            // 遍历查询结果集
            while(rs.next()){
                String name = rs.getString("name");
                System.out.println(name);
            }
        } catch(Exception e) {
            e.printStackTrace();
        } finally {
            // ...省略释放资源的代码
        }
    }
}

In JdbcTestthe mainfunction call DriverManagerof getConnectiontime method, performed this time is bound to DriverManagera static class code block code, and then execute getConnectionthe method, the first look at DriverManagerthe static code block:

// DriverManager.java

static {
    // 加载驱动实现类
    loadInitialDrivers();
    println("JDBC DriverManager initialized");
}

Continue to follow up loadInitialDriversthe code:

// DriverManager.java

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;
    }
    AccessController.doPrivileged(new PrivilegedAction<Void>() {
        public Void run() {
            // 来到这里,是不是感觉似曾相识,对,没错,我们在前面的JdkSPITest代码中执行过下面的两句代码
            // 这句代码前面已经分析过,这里不会真正加载服务提供者实现类
            // 而是实例化一个ServiceLoader对象且实例化一个LazyIterator对象用于懒加载
            ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
            // 调用ServiceLoader的iterator方法,在迭代的同时,也会去加载并实例化META-INF/services/java.sql.Driver文件
            // 的com.mysql.jdbc.Driver和com.mysql.fabric.jdbc.FabricMySQLDriver两个驱动类
            /****************【主线,重点关注】**********************/
            Iterator<Driver> driversIterator = loadedDrivers.iterator();
            try{
                while(driversIterator.hasNext()) {
                    driversIterator.next();
                }
            } catch(Throwable t) {
            // Do nothing
            }
            returnnull;
        }
    });

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

In the above code, we can see Mysql drive mainly using Java class loading mechanism implemented in SPI, i.e. use ServiceLoaderto achieve load and instantiate the driver class Mysql.

5.2 Registration Mysql driving class

So, just above code to load and instantiate the class Mysql driver, then the driver class and how to be registered into DriverManagerthe registeredDriverscollection of it?

At this point, we note that com.mysql.jdbc.Driverthe class which also has a static block of code, that will certainly trigger the execution of the static code block of code when you instantiate the class, then we look at the direct static block of code to do something:

// com.mysql.jdbc.Driver.java

// Register ourselves with the DriverManager
static {
    try {
        // 将自己注册进DriverManager类的registeredDrivers集合
        java.sql.DriverManager.registerDriver(new Driver());
    } catch (SQLException E) {
        thrownew RuntimeException("Can't register driver!");
    }
}

It can be seen, but it was Mysql driver class com.mysql.jdbc.Driveris instantiated when its use when the time to perform static code block will register itself into DriverManagerthe registeredDriverscollection.

Well, continue to follow DriverManagerthe registerDrivermethod:

// DriverManager.java

public static synchronized void registerDriver(java.sql.Driver driver)
    throws SQLException {
    // 继续调用registerDriver方法
    registerDriver(driver, null);
}

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) {
        // 将driver驱动类实例注册进registeredDrivers集合
        registeredDrivers.addIfAbsent(new DriverInfo(driver, da));
    } else {
        // This is for compatibility with the original DriverManager
        thrownew NullPointerException();
    }
    println("registerDriver: " + driver);
}

Analysis here, and we understand how Java is loaded drive mechanism of SPI class Mysql and Mysql how to drive class registration into DriverManagerthe registeredDriverscollection.

Previously registered 5.3 Mysql database connection driver class

Since Mysql driver class has been registered come in, then when will it be used?

We want to connect Mysql database, the natural need to use the driver class Mysql, right. At this point we return to the test code JDBC JdbcTestclass of connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbc", "root", "123456");this code, a look at the getConnectionsource code:

// DriverManager.java

@CallerSensitive
public static Connection getConnection(String url,
    String user, String password) throws SQLException {
    java.util.Properties info = new java.util.Properties();

    if (user != null) {
        info.put("user", user);
    }
    if (password != null) {
        info.put("password", password);
    }
    // 继续调用getConnection方法来连接数据库
    return (getConnection(url, info, Reflection.getCallerClass()));
}

Continue to follow up getConnectionmethod:

// DriverManager.java

private static Connection getConnection(
        String url, java.util.Properties info, Class<?> caller) throws SQLException {
        
        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) {
            thrownew 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;
        // 遍历registeredDrivers集合,注意之前加载的Mysql驱动类实例被注册进这个集合
        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());
                    // 利用Mysql驱动类来连接数据库
                    /*************【主线,重点关注】*****************/
                    Connection con = aDriver.driver.connect(url, info);
                    // 只要连接上,那么加载的其余驱动类比如FabricMySQLDriver将会忽略,因为下面直接返回了
                    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);
        thrownew SQLException("No suitable driver found for "+ url, "08001");
    }

Can be seen, DriverManagerthe getConnectionmethod will be from registeredDriversout Mysql just loaded class driver set is connected to the database.

Well, here, JDBC drivers loaded on the basic source code analysis finished.

6 thread context class loader

The basic analysis done earlier source JDBC driver is loaded, but there is a very important knowledge not explain that parents undermine class loading mechanism of the delegation model thread context class loader .

As we all know, JDBC-related class specifications (such as front java.sql.Driverand java.sql.DriverManager) are in Jdk the rt.jarlower cladding, means that the class will be class loader (BootstrapClassLoader) Load Startup; and Mysql class implements driven by an external database vendors, when driving when the class is to introduce the project is located in the project classpath, in which case the boot class loader is certainly not possible to load these drivers like ah, this time how to do?

As the parent class loading mechanism delegation defect model in this regard, we can only break a parent delegation model. Because the project classpathin class is to be loaded by the application class loader (AppClassLoader), so can we "reverse" Let's start the class loader delegate application class loader to load these external database vendor driver class it? If so, how can we do so that we start the class loader delegate application class loader to load classpathclasses in it?

The answer is definitely yes, we can set the application class loader into the inside thread, the thread that is inside a newly defined attribute class loader contextClassLoader, then at a certain time to the application class loader is set into the thread of contextClassLoaderthe property inside, If no, then the default is the application class loader.

And then start the class loader to load java.sql.Driverand java.sql.DriverManagerwhen other types, but also removed from the current thread contextClassLoaderthat is application class loader to classpathload JDBC driver class provided by outside vendors. Therefore, the delegation model by destroying the class loading mechanism of parents, using the thread context class loader is the perfect solution to the problem.

At this point we go back and look at what time get thread context class loader when loading Mysql drive it?

The answer is in DriverManagerthe loadInitialDriversmethod calls ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);this code, and remove the thread context class loader is in ServiceLoaderthe loadapproach taken:

publicstatic <S> ServiceLoader<S> load(Class<S> service) {
    // 取出线程上下文类加载器取出的是contextClassLoader,而contextClassLoader装的应用程序类加载器
    ClassLoader cl = Thread.currentThread().getContextClassLoader();
    // 把刚才取出的线程上下文类加载器作为参数传入,用于后去加载classpath中的外部厂商提供的驱动类
    return ServiceLoader.load(service, cl);
}

So, here, and we understand that acts as a thread context class loader to load JDBC driver package in the role. In addition, we should know, Java vast majority involve the use of SPI are loaded thread context class loader to complete, such as JNDI, JCE, JBI and so on.

Extended : Breaking the class loading mechanism parent delegation model as well as hot deployment code, etc. In addition, Tomcat's class loading mechanism is also worth reading.

Note: If parents some minor partner of the delegation model class loading mechanism is not clear, it is recommended to fully understand the parents and delegation model in custom ClassLoader understand this text.

7 extension: Dubbo mechanism of SPI

Also talked about Dubbo front frame body is everywhere SPI application mechanism, it can be said everywhere extension point, really is the SPI application mechanism of the head. But Dubbo did not use the default Java mechanism of SPI, but their mechanism to achieve a set of SPI.

So, Dubbo Why not use the Java SPI mechanism?

There are two main reasons:

  1. Java's SPI mechanism will be a one-time extension point to instantiate all realize, if extension initialization achieve very time-consuming, but also useless if loaded, would be a waste of resources;

  2. The mechanism does not support Java SPI Ioc and AOP, so Dubbo with its own SPI mechanisms: increased support for the extension points of IoC and AOP, an extension point can be directly injected into the other setter extension points.

For these reasons, Dubbo from SPI defines a set of mechanisms for loading their own extension points. About SPI mechanism Dubbo's not go into details here, small partners who are interested can go to Dubbo official website to see how to extend the SPI Dubbo? As well as its official website also has Duboo of SPI source code analysis of the article.

8 Summary

Well, Java's SPI mechanism to interpret it here, in front of the first knowledge and then summarize:

1, the use of Java SPI mechanisms;

2 principle, Java's SPI mechanisms;

3, JDBC driver loading principle;

4, the SPI briefly Duboo mechanism.

Article from: Huperzine architecture notes

Published 306 original articles · won praise 67 · views 380 000 +

Guess you like

Origin blog.csdn.net/ailiandeziwei/article/details/105287950