JavaSPI(二)ServiceLoader深入解析

SPI(Service Provider Interface) ,是JDK内置的一种服务提供发现机制,说到JavaSPI的服务提供发现机制,就必须要了解ServiceLoader的原理。在ServiceLoader使用到Thread.currentThread().getContextClassLoader(),线程上下文类加载器来加载发现的服务类。

1、线程上下文类加载器

ContextClassLoader是从JDK1.2开始引入的。类java.lang.Thread 中的方法getContextClassLoader()和setContextClassLoader(ClassLoader cl)用来获取和设置线程的上下文类加载器。

如果没有通过setContextClassLoader(ClassLoader cl)方法进行设置的话,线程将继承其父线程的上下文类加载器。Java应用运行的初始线程的上下文类加载器是系统类加载器。在线程中运行的代码可以通过此类加载器来加载类和资源。

为什么会有线程上下文类加载器Thread context class loader?存在的目的主要是为了解决双亲委派(parent delegation)机制下无法干净的解决的问题。

假如有下述委派链:

ClassLoader A -> System class loader -> Extension class loader -> Bootstrap class loader

委派链左边的ClassLoader就可以很自然的使用右边的ClassLoader所加载的类。

但如果情况要反过来,是右边的Bootstrap class loader所加载的代码需要反过来去找委派链靠左边的ClassLoaderA去加载东西怎么办呢?没辙,parent delegation是单向的,没办法反过来从右边找左边。

这种情况下就可以把某个位于委派链左边的ClassLoader设置为线程的context class loader,这样就给机会让代码不受parent delegation的委派方向的限制而加载到类了。

2、JDBC接口实例分析

看如下代码:

package com.alicharles; import java.sql.Driver;
import java.sql.DriverManager;
import java.util.Enumeration;

public class TestMain {
    public static void main(String[] args) throws Exception {
        Enumeration<Driver> drivers = DriverManager.getDrivers();
        while (drivers.hasMoreElements()) {
            Driver driver = (Driver) drivers.nextElement();
            System.out.println(driver.getClass() + " : " + driver.getClass().getClassLoader());
        }
        System.out.println(Thread.currentThread().getContextClassLoader());
        System.out.println(DriverManager.class.getClassLoader());
    }
}

输出结果如下:

class com.mysql.jdbc.Driver : sun.misc.Launcher$AppClassLoader@232204a1
class com.mysql.fabric.jdbc.FabricMySQLDriver : sun.misc.Launcher$AppClassLoader@232204a1
sun.misc.Launcher$AppClassLoader@232204a1
null

我们从结果可以看到,我们并没有注册Driver然后就可以获得到Dirver,并且还可以看到他们的类加载器都是系统加载器AppClassLoader。

而DriverManager 的类加载器是null,也就是说他的加载器是Bootstrap class loader。那我们看下DriverManager源码

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

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() {
            // 1.通过ServiceLoader获取Driver的SPI的实现类配置
            ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
            // 2.获取ServiceLoader的Iterator
            Iterator<Driver> driversIterator = loadedDrivers.iterator();
            try{
                // 3.hasNext是否有下一个服务实现类提供者
                while(driversIterator.hasNext()) {
                    // next加载服务实现类到VM,通过class.forName,并实例化
                    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);
        }
    }
}

1. 通过ServiceLoader获取Driver的SPI的实现类配置

看下ServiceLoader 是如何进行加载的。

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

依赖的构造方法以及 reload 方法
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();
}

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

其中ServiceLoader 的属性如下

//SPI的默认路径
private static final String PREFIX = "META-INF/services/";
// 被加载的接口或者类
private final Class<S> service;
// 类加载器,传入的是线程上下文类加载器
private final ClassLoader loader;
// 访问控制上下文
private final AccessControlContext acc;
// 缓存的服务提供者
private LinkedHashMap<String,S> providers = new LinkedHashMap<>();
// The current lazy-lookup iterator
private LazyIterator lookupIterator;

2. 获取ServiceLoader的Iterator

其中的hasNext()和next()方法依赖LazyIterator的实例lookupIterator,

对LazyIterator进行了一层包装,每次在迭代的时候会把发现的提供者加入到 ServiceLoader内部的一个map当中。

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

        public boolean hasNext() {
            if (knownProviders.hasNext())
                return true;
            return lookupIterator.hasNext();
        }

        public S next() {
            if (knownProviders.hasNext())
                return knownProviders.next().getValue();
            return lookupIterator.next();
        }

        public void remove() {
            throw new UnsupportedOperationException();
        }
    };
}

3. hasNext和next实现

其中hasNext判断是否有下一个服务实现类提供者,

next通过class.forName方法加载服务实现类到VM并实例化,

hasNext和next方法依赖LazyIterator懒加载的迭代器的实现。

LazyIterator类源代码实现如下

private class 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;
    }

    private boolean hasNextService() {
        if (nextName != null) {
            return true;
        }
        if (configs == null) {
            try {
                // 加载com.sql.Driver的SPI的服务实现类列表文件
                String fullName = PREFIX + service.getName();
                if (loader == null)
                    configs = ClassLoader.getSystemResources(fullName);
                else
                    configs = loader.getResources(fullName);
            } catch (IOException x) {
                fail(service, "Error locating configuration files", x);
            }
        }
        while ((pending == null) || !pending.hasNext()) {
            if (!configs.hasMoreElements()) {
                return false;
            }
            pending = parse(service, configs.nextElement());
        }
        nextName = pending.next();
        return true;
    }

    private S nextService() {
        if (!hasNextService())
            throw new NoSuchElementException();
        String cn = nextName;
        nextName = null;
        Class<?> c = null;
        try {
            // 加载服务实现类到VM,通过class.forName
            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 {
            // 实例化服务实现类
            S p = service.cast(c.newInstance());
            // 把发现的提供者加入到ServiceLoader内部的providers中
            providers.put(cn, p);
            return p;
        } catch (Throwable x) {
            fail(service, "Provider " + cn + " could not be instantiated", x);
        }
        throw new Error();          // This cannot happen
    }

    // 3.调用hasNextService ()判断是否有下一个服务实现类提供者
    public boolean hasNext() {
        if (acc == null) {
            return hasNextService();
        } else {
            PrivilegedAction<Boolean> action = new PrivilegedAction<Boolean>() {
                public Boolean run() { return hasNextService(); }
            };
            return AccessController.doPrivileged(action, acc);
        }
    }
    // 3.调用nextService()通过class.forName加载服务实现类到VM并实例化
    public S next() {
        if (acc == null) {
            return nextService();
        } else {
            PrivilegedAction<S> action = new PrivilegedAction<S>() {
                public S run() { return nextService(); }
            };
            return AccessController.doPrivileged(action, acc);
        }
    }

    public void remove() {
        throw new UnsupportedOperationException();
    }
}

其中加载的com.mysql.jdbc.Driver的源代码如下

package com.mysql.jdbc;
import com.mysql.jdbc.NonRegisteringDriver;
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!");
        }
    }
}

回到JDBC的那个例子当中,为什么我们会自动进行注册呢。

1、DriverManager在类加载的时候,初始化静态块调用loadInitialDrivers()方法;

2、在loadInitialDrivers方法中调用driversIterator.next()方法;

3、最终调用LazyIterator类的nextService(),通过class.forName加载服务实现类到VM,并实例化;

4、Driver初始化的时候,将当前类注册到DriverManager的CopyOnWriteArrayList<DriverInfo>类型的registeredDrivers静态属性上,供getDrivers()方法获取,从而加载的所有的服务实现类。

文章目录
  1. 1、线程上下文类加载器
  2. 2、JDBC接口实例分析
    1. 1. 通过ServiceLoader获取Driver的SPI的实现类配置
    2. 2. 获取ServiceLoader的Iterator
    3. 3. hasNext和next实现