JDBC使用SPI
public class T01SPI {
public static void main(String[] args) {
Enumeration<Driver> drivers = DriverManager.getDrivers();
Driver driver;
while (drivers.hasMoreElements()) {
driver = drivers.nextElement();
System.out.println(driver.getClass() + "==>类加载器: " + driver.getClass().getClassLoader());
}
System.out.println("DriverManager的类加载器:" + DriverManager.class.getClassLoader());
}
}
输出结果如下:
class com.mysql.cj.jdbc.Driver==>类加载器: sun.misc.Launcher$AppClassLoader@18b4aac2
class com.alibaba.druid.proxy.DruidDriver==>类加载器: sun.misc.Launcher$AppClassLoader@18b4aac2
class com.alibaba.druid.mock.MockDriver==>类加载器: sun.misc.Launcher$AppClassLoader@18b4aac2
class org.h2.Driver==>类加载器: sun.misc.Launcher$AppClassLoader@18b4aac2
DriverManager的类加载器:null
DriverManager使用的类加载器为null,即通过Bootstrap ClassLoader加载进来的; 而com.mysql.jdbc.Driver是通过Application ClassLoader加载进来的。
由于双亲委派模型,父加载器是拿不到通过子加载器加载的类的。
首先用户自定义的SPI拓展肯定不能通过Bootstrap ClassLoader加载,因为它又不是系统类. 正常通过Application ClassLoader加载.
这里就引出了一个问题,为什么通过Bootstrap ClassLoader加载进来的DriverManager,可以拿到Application ClassLoader加载进来的com.mysql.jdbc.Driver ?
这个现象,就是破坏了双亲委派模型。
为什么要破坏双亲委派模型
因为DriverManager是通过Bootstrap ClassLoader加载进来的, 而com.mysql.jdbc.Driver是通过classpath的JAR包加载进来的。 要想通过DriverManager获得 Driver 的具体实现,必须破坏双亲委派模型才能拿到。
DriverManager是如何拿到com.mysql.jdbc.Driver类的
通过Thread.currentThread().setContextClassLoader(), 将Application ClassLoader设置为线程上下文加载器。 在DriverManager类里通过Thread.currentThread().getContextClassLoader() 拿到Application ClassLoader进行加载。
DriverManager静态代码块
static {
loadInitialDrivers();
println("JDBC DriverManager initialized");
}
loadInitialDrivers()里
ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
Iterator<Driver> driversIterator = loadedDrivers.iterator();
java.util.ServiceLoader
public static <S> ServiceLoader<S> load(Class<S> service) {
ClassLoader cl = Thread.currentThread().getContextClassLoader();
return ServiceLoader.load(service, cl);
}
load:537, ServiceLoader (java.util)
run:586, DriverManager$2 (java.sql)
run:583, DriverManager$2 (java.sql)
doPrivileged:-1, AccessController (java.security)
loadInitialDrivers:583, DriverManager (java.sql)
<clinit>:101, DriverManager (java.sql)
main:13, T01SPI (com.zc.basic.z18spi)
入口: ServiceLoader.load()
默认线程创建时候的上下文类加载器就是 Launcher$AppClassLoader
ServiceLoader.load() 方法里全程用的是 传入的 上下文类加载器.
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);
}
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);
}
属性
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<>();
private LazyIterator lookupIterator;
现在我们发现重点就在于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 {
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 Iterator<String> parse(Class<?> service, URL u)
throws ServiceConfigurationError {
InputStream in = null;
BufferedReader r = null;
ArrayList<String> names = new ArrayList<>();
try {
in = u.openStream();
r = new BufferedReader(new InputStreamReader(in, "utf-8"));
int lc = 1;
while ((lc = parseLine(service, u, r, lc, names)) >= 0);
} catch (IOException x) {
fail(service, "Error reading configuration file", x);
} finally {
try {
if (r != null) r.close();
if (in != null) in.close();
} catch (IOException y) {
fail(service, "Error closing configuration file", y);
}
}
return names.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");
}
if (!service.isAssignableFrom(c)) {
fail(service,
"Provider " + cn + " not a subtype");
}
try {
S p = service.cast(c.newInstance());
providers.put(cn, p);
return p;
} catch (Throwable x) {
fail(service,
"Provider " + cn + " could not be instantiated",
x);
}
throw new Error();
}
|