阅读源码学习JDBC

场景

maven配置

      
        mysql
        mysql-connector-java
        5.1.41
      

java代码

public static void main(String[] args)
{
    Enumeration drivers = DriverManager.getDrivers();
    Driver driver;
    while (drivers.hasMoreElements())
    {
        driver = drivers.nextElement();
        System.out.println(driver.getClass() + "------" + driver.getClass().getClassLoader());
    }
    System.out.println(DriverManager.class.getClassLoader());
}

输出结果如下:

class com.mysql.jdbc.Driver------sun.misc.Launcher$AppClassLoader@2a139a55
class com.mysql.fabric.jdbc.FabricMySQLDriver------sun.misc.Launcher$AppClassLoader@2a139a55
null

问题

JDBC SPI是如何初始化的?

1. DriverManager类

首先,我们看到main方法是调用了DriverManager方法拿到mysql的驱动类的。

public class 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");
    }
}

DriverManager类在加载时,会调用loadInitialDrivers()方法。

    private static void loadInitialDrivers() {
...
        // 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);
...

loadInitialDrivers()方法会调用ServiceLoader.load(Driver.class)。这个方法是让ServiceLoader去加载Driver.class的所有实现类。

2. ServiceLoader类

public final class ServiceLoader
    implements Iterable
{

    private static final String PREFIX = "META-INF/services/";
    /**
     * Clear this loader's provider cache so that all providers will be
     * reloaded.
     *
     * 

After invoking this method, subsequent invocations of the {@link * #iterator() iterator} method will lazily look up and instantiate * providers from scratch, just as is done by a newly-created loader. * *

This method is intended for use in situations in which new providers * can be installed into a running Java virtual machine. */ public void reload() { providers.clear(); lookupIterator = new LazyIterator(service, loader); } private ServiceLoader(Class 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是怎么知道要去加载哪些类呢?
这里的秘密就在于Driver.class。ServiceLoader会去根据要加载的接口类读取特定目录下的配置文件。这里为 "META-INF/services/" + Driver.class,即"META-INF/services/java.sql.Driver"文件。
例如mysql-connector-java-5.1.41.jar!/META-INF/services/java.sql.Driver的内容为:

com.mysql.jdbc.Driver
com.mysql.fabric.jdbc.FabricMySQLDriver

就是告诉ServiceLoader,我这个包实现了这两个Driver,你去加载一下吧。

3. LazyIterator

ServiceLoader初始化以后,会持有一个LazyIterator迭代器。

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

DriverManager拿到ServiceLoader实例以后,会对其进行迭代以获得Driver的实现类。

    private class LazyIterator
        implements Iterator
    {
        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");
            }
...

核心代码是Class.forName(cn, false, loader),它会动态加载Driver的实现类。

4. Driver类

这里的Driver类指的是mysql的实现类,完整包名为com.mysql.jdbc.Driver

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

可以看到,最后Driver通过调用DriverManager.registerDriver(new Driver()),将自身实例注册回了DriverManager。

这里同时解释了一个现象,在第三步,为什么用Class.forName(),而不用ClassLoader.loadClass()。因为ClassLoader.loadClass()不会初始化静态块。用了ClassLoader.loadClass(),就无法将自身实例注入到DriverManager中去了。

关键点总结:

  • DriverManager
  • Driver.class
  • ServiceLoader
  • META-INF/services/java.sql.Driver

参考资料:jdbc 类加载器,与 spi 服务机制

你可能感兴趣的:(阅读源码学习JDBC)