SPI 之(JDBC)

DriverManager

通过DriverManager获取连接

我们在获取连接数据库的时候会使用到如下代码

Connection conn = DriverManager.getConnection("url", "user", "password");

DriverManager的初始化

在调用DriverManager.getConnection的时候,首先会对DriverManager执行static代码块进行初始化

static {
    loadInitialDrivers();
    println("JDBC DriverMa在这里插入代码片nager initialized");
}
private static void loadInitialDrivers() {
    String drivers;
    try {
        //AccessController.doPrivileged 只是和权限控制有关,这里不做讲解
        drivers = AccessController.doPrivileged(new PrivilegedAction<String>() {
            public String run() {
                //通过系统变量去获取值
                return System.getProperty("jdbc.drivers");
            }
        });
    } catch (Exception ex) {
        drivers = null;
    }
    //使用spi的方式進行服务驱动注冊(下面将对spi进行详细讲解)
    AccessController.doPrivileged(new PrivilegedAction<Void>() {
        public Void run() {
            //spi服务初始化
            ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
            //获取ServiceLoader的迭代器(在下面会详细介绍)
            Iterator<Driver> driversIterator = loadedDrivers.iterator();
            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;
    }
    //如果通过系统变量获取到drivers的值,则通过Class.forName()进行初始化
    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);
        }
    }
}

SPI

什么是SPI

SPI,全称Service Provider Interfaces,服务提供接口。是Java提供的一套供第三方实现或扩展使用的技术体系。主要通过解耦服务具体实现以及服务使用,使得程序的可扩展性大大增强,甚至无耦合(例子:需要的时候导入依赖,不需要的时候删除依赖即可,不需要其他操作)。

如何实现

JDK中,基于SPI的思想,提供了默认具体的实现,ServiceLoader。它会去扫描读取 ’ META-INF/services/ '目录下的文件名为提供服务的接口的全类名的file,通过线程上下文加载去加载文件里保存的服务实现类的全类名(Class.forName()的方式)

//此方法在前面DriverManager的初始化会调用此方法来初始化ServiceLoader
ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
//load方法
public static <S> ServiceLoader<S> load(Class<S> service) {
    //获取线程上下文类加载器
    //这里为什么要使用线程上下文加载器(后面会进行说明)
    ClassLoader cl = Thread.currentThread().getContextClassLoader();
    //初始化ServiceLoader实例
    //让线程上下文类加载器对service进行加载
    return ServiceLoader.load(service, cl);
}

public static <S> ServiceLoader<S> load(Class<S> service,ClassLoader loader)
{
    return new ServiceLoader<>(service, loader);
}
//ServiceLoader构造器
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();
}
//providers为 LinkedHashMap<String,S> providers = new LinkedHashMap<>();
//providers 保存了注册的服务驱动实例
//lookupIterator 创建了LazyIterator
public void reload() {
    providers.clear();
	//初始化懒加载迭代器,服务的初始化和实例化都在这个类中实现
	//这里初始化保存了service,和loader
    lookupIterator = new LazyIterator(service, loader);
}
//获取iterator,前面DriverManager介绍中,初始化ServiceLoader后会调用此方法来获取Iterator,然后对它进行迭代
//(Iterator<Driver> driversIterator = loadedDrivers.iterator();)

//创建iterator迭代器
public Iterator<S> iterator() {
    return new Iterator<S>() {
        //如果是第一次迭代knownProviders没有数据,因为开始了调用reload
        //这里不明白请从头开始认真看一遍
        Iterator<Map.Entry<String,S>> knownProviders
            = providers.entrySet().iterator();
        
        public boolean hasNext() {
            if (knownProviders.hasNext())
                return true;
            //lookupIterator = new LazyIterator(service, loader);
            return lookupIterator.hasNext();
        }

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

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

    };
}
//懒加载迭代器
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;
    }
    //将下一个服务名付给nextName 
    private boolean hasNextService() {
        //第一次迭代的华nextName为null;
        if (nextName != null) {
            return true;
        }
        //第一次迭代的华configs为null;
        if (configs == null) {
            try {
                //拼接字符串得到类的相对路径
                //PREFIX = "META-INF/services/"
                String fullName = PREFIX + service.getName();
                if (loader == null)
                    //读取所有fullName下的文件
                    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;
            }
            //为什么是个Iterator,因为一个文件里可能会配置多个Driver
            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 {
            //加载 nextName 类,不初始化
            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中,LinkedHashMap<String,S> providers = new LinkedHashMap<>();
            providers.put(cn, p);
            将实例返回
            return p;
        } catch (Throwable x) {
            fail(service,
                 "Provider " + cn + " could not be instantiated",
                 x);
        }
        throw new Error();          // This cannot happen
    }

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

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

}

JDBC驱动的注册

JDBC所有的服务实现都会保存到CopyOnWriteArrayList里面,CopyOnWriteArrayList是DriverManager里面的一个类变量。

private final static CopyOnWriteArrayList<DriverInfo> registeredDrivers = new CopyOnWriteArrayList<>();

那么是如何保存的呢,下面的代码是mysql对Driver的实现

public class Driver extends NonRegisteringDriver implements java.sql.Driver {
    public Driver() throws SQLException {
    }
	//在spi里对Driver进行实例化的时候会先进行下面的初始化
    static {
        try {
        	//通过DriverManager的registerDriver对驱动进行保存
            DriverManager.registerDriver(new Driver());
        } catch (SQLException var1) {
            throw new RuntimeException("Can't register driver!");
        }
    }
}

下面是registerDriver的实现

public static synchronized void registerDriver(java.sql.Driver driver)
    throws SQLException {

    registerDriver(driver, null);
}

public static synchronized void registerDriver(java.sql.Driver driver,
        DriverAction da)
    throws SQLException {

    if(driver != null) {
    	//将Driver保存到registeredDrivers里面
    	//registeredDrivers为前面说的CopyOnWriteArrayList
        registeredDrivers.addIfAbsent(new DriverInfo(driver, da));
    } else {
        throw new NullPointerException();
    }

    println("registerDriver: " + driver);

}

线程上下文加载器

为什么要用线程上下文加载器

DriverManager和ServiceLoader都在rt.jar包下,所以它是由BootstrapClassLoader来进行加载的,当我们在这些类中对一个类进行加载的时候会使用当前类的类加载器,mysql驱动是以jar包的形式放在classpath中的,BootstrapClassLoader加载不到(为什么:因为类加载器只能委托父加载器进行加载,而BootstrapClassLoader没有父加载器,不要问为什么不能让子加载器进行加载,因为此时BootstrapClassLoader就是最底层的子加载器),只有ApplicationClassLoader能进行加载,那我们如何获取ApplicationClassLoader呢,那就是使用线程上下文加载器,它默认保存的就是ApplicationClassLoader。使用它就可以使用ApplicationClassLoader 来进行加载了(这也就是为什么说线程上下问加载器破坏了双亲委派机制的原因)下面是laucher类的构造器

public Launcher() {
        Launcher.ExtClassLoader var1;
        try {
        	//获取ExtClassLoader
            var1 = Launcher.ExtClassLoader.getExtClassLoader();
        } catch (IOException var10) {
            throw new InternalError("Could not create extension class loader", var10);
        }

        try {
        	//获取ApplicationClassLoader,并将ExtClassLoader设置为它的父加载器
            this.loader = Launcher.AppClassLoader.getAppClassLoader(var1);
        } catch (IOException var9) {
            throw new InternalError("Could not create application class loader", var9);
        }
        //设置线程上下文加载器为ApplicationClassLoader
        Thread.currentThread().setContextClassLoader(this.loader);
        String var2 = System.getProperty("java.security.manager");
        if (var2 != null) {
            SecurityManager var3 = null;
            if (!"".equals(var2) && !"default".equals(var2)) {
                try {
                    var3 = (SecurityManager)this.loader.loadClass(var2).newInstance();
                } catch (IllegalAccessException var5) {
                } catch (InstantiationException var6) {
                } catch (ClassNotFoundException var7) {
                } catch (ClassCastException var8) {
                }
            } else {
                var3 = new SecurityManager();
            }

            if (var3 == null) {
                throw new InternalError("Could not create SecurityManager: " + var2);
            }

            System.setSecurityManager(var3);
        }

    }

你也可以使用下面这种方式对线程上下问加载器进行设置(这里设置为ExtClassLoader)

Thread.currentThread().setContextClassLoader(Test.class.getClassLoader().getParent());

此时我们再执行DriverManager.getConnection()的时候,会抛出一下错误

Exception in thread "main" java.sql.SQLException: No suitable driver found for jdbc:mysql://127.0.0.1:3306/mall_product?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
	at java.sql.DriverManager.getConnection(DriverManager.java:689)
	at java.sql.DriverManager.getConnection(DriverManager.java:247)
	at com.heky.mysql.ThreadContextClassLoaderTest.main(ThreadContextClassLoaderTest.java:28)

原因是ExtClassLoader无法加载到mysql的jar包

  • 4
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值