聊聊JDBC是如何破坏双亲委派模型的

www.jianshu.com/p/60dbd8009…
java

资源来源《深刻理解Java虚拟机》mysql

双亲委派模型的第一次“被破坏”其实发生在双亲委派模型出现以前--即JDK1.2发布以前。因为双亲委派模型是在JDK1.2以后才被引入的,而类加载器和抽象类java.lang.ClassLoader则是JDK1.0时候就已经存在,面对已经存在 的用户自定义类加载器的实现代码,Java设计者引入双亲委派模型时不得不作出一些妥协。为了向前兼容,JDK1.2以后的java.lang.ClassLoader添加了一个新的proceted方法findClass(),在此以前,用户去继承java.lang.ClassLoader的惟一目的就是重写loadClass()方法,由于虚拟在进行类加载的时候会调用加载器的私有方法loadClassInternal(),而这个方法的惟一逻辑就是去调用本身的loadClass()。JDK1.2以后已再也不提倡用户再去覆盖loadClass()方法,应当把本身的类加载逻辑写到findClass()方法中,在loadClass()方法的逻辑里,若是父类加载器加载失败,则会调用本身的findClass()方法来完成加载,这样就能够保证新写出来的类加载器是符合双亲委派模型的。
双亲委派模型的第二次“被破坏”是这个模型自身的缺陷所致使的,双亲委派模型很好地解决了各个类加载器的基础类统一问题(越基础的类由越上层的加载器进行加载),基础类之因此被称为“基础”,是由于它们老是做为被调用代码调用的API。可是,若是基础类又要调用用户的代码,那该怎么办呢。
这并不是是不可能的事情,一个典型的例子即是JNDI服务,它的代码由启动类加载器去加载(在JDK1.3时放进rt.jar),但JNDI的目的就是对资源进行集中管理和查找,它须要调用独立厂商实现部部署在应用程序的classpath下的JNDI接口提供者(SPI, Service Provider Interface)的代码,但启动类加载器不可能“认识”之些代码,该怎么办?
为了解决这个困境,Java设计团队只好引入了一个不太优雅的设计:线程上下文件类加载器(Thread Context ClassLoader)。这个类加载器能够经过java.lang.Thread类的setContextClassLoader()方法进行设置,若是建立线程时还未设置,它将会从父线程中继承一个;若是在应用程序的全局范围内都没有设置过,那么这个类加载器默认就是应用程序类加载器。了有线程上下文类加载器,JNDI服务使用这个线程上下文类加载器去加载所须要的SPI代码,也就是父类加载器请求子类加载器去完成类加载动做,这种行为实际上就是打通了双亲委派模型的层次结构来逆向使用类加载器,已经违背了双亲委派模型,但这也是迫不得已的事情。Java中全部涉及SPI的加载动做基本上都采用这种方式,例如JNDI,JDBC,JCE,JAXB和JBI等。sql

第一种比较简单,这里就不说啦。数据库

关于第二种,看看JDBC中是怎么实现的吧bash

首先,理解一下为何JDBC须要破坏双亲委派模式,缘由是原生的JDBC中Driver驱动自己只是一个接口,并无具体的实现,具体的实现是由不一样数据库类型去实现的。例如,MySQL的mysql-connector-app

.jar中的Driver类具体实现的。 原生的JDBC中的类是放在rt.jar包的,是由启动类加载器进行类加载的,在JDBC中的Driver类中须要动态去加载不一样数据库类型的Driver类,而mysql-connector-
.jar中的Driver类是用户本身写的代码,那启动类加载器确定是不能进行加载的,既然是本身编写的代码,那就须要由应用程序启动类去进行类加载。因而乎,这个时候就引入线程上下文件类加载器(Thread Context ClassLoader)。有了这个东西以后,程序就能够把本来须要由启动类加载器进行加载的类,由应用程序类加载器去进行加载了。下面看看JDBC中是怎么去应用的呢

//callerCL为空的时候,其实说明这个ClassLoader是启动类加载器,可是这个启动类加载并不能识别rt.jar以外的类,这个时候就把callerCL赋值为Thread.currentThread().getContextClassLoader();也就是应用程序启动类ide

private static Connection getConnection(
        String url, java.util.Properties info, Class<?> caller) throws SQLException {
        /*
         * When callerCl is null, we should check the application's * (which is invoking this class indirectly) * classloader, so that the JDBC driver class outside rt.jar * can be loaded from here. */ //callerCL为空的时候,其实说明这个ClassLoader是启动类加载器,可是这个启动类加载并不能识别rt.jar以外的类,这个时候就把callerCL赋值为Thread.currentThread().getContextClassLoader();也就是应用程序启动类 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) { throw new 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; 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()); Connection con = aDriver.driver.connect(url, info); 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); throw new SQLException("No suitable driver found for "+ url, "08001"); } private static boolean isDriverAllowed(Driver driver, ClassLoader classLoader) { boolean result = false; if(driver != null) { Class<?> aClass = null; try { //这一步会对类进行初始化的动做,而初始化以前天然也要进行的类的加载工做 aClass = Class.forName(driver.getClass().getName(), true, classLoader); } catch (Exception ex) { result = false; } result = ( aClass == driver.getClass() ) ? true : false; } return result; } 复制代码

但愿个人理解能给你带来帮助,若有错误的地方请指正。ui

相关文章
相关标签/搜索