JDBC Driver加载时是怎么破坏双亲委派的、它是怎么实现的,之前又是怎么加载的?

一、JDBC之前加载驱动的方式

在说破坏双亲委派之前,先看下之前是怎么加载Driver的。在刚开始的时候JDBC在加载class的时候,其实是直接利用了Class.classforName

Class.forName()和ClassLoader.loadClass区别
Class.forName(className)方法,内部实际调用的方法是  Class.forName(className,true,classloader);

第2个boolean参数表示类是否需要初始化,  Class.forName(className)默认是需要初始化。

一旦初始化,就会触发目标对象的 static块代码执行,static参数也也会被再次初始化。

    

ClassLoader.loadClass(className)方法,内部实际调用的方法是  ClassLoader.loadClass(className,false);

第2个 boolean参数,表示目标对象是否进行链接,false表示不进行链接,由上面介绍可以,

不进行链接意味着不进行包括初始化等一些列步骤,那么静态块和静态对象就不会得到执行
//加载Oracle数据库驱动 
Driver driver = (Driver)Class.forName("oracle.jdbc.driver.OracleDriver"); 
   
//加载SQL Server数据库驱动 
Driver driver = (Driver)Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver"); 
   
//加载MySQL 数据库驱动 
Driver driver = (Driver)Class.forName("com.mysql.jdbc.Driver");

Class.forName()将对应的驱动类加载到内存中,然后执行内存中的static静态代码段,代码段中,会创建一个驱动Driver的实例,放入DriverManager中,供DriverManager使用。

static { 
    Timestamp localTimestamp = Timestamp.valueOf("2000-01-01 00:00:00.0"); 
    try { 
        if (defaultDriver == null) { 
            //创建一个OracleDriver实例,然后注册到DriverManager中 
            defaultDriver = new OracleDriver(); 
            DriverManager.registerDriver(defaultDriver); 
        } 
 
    } catch (RuntimeException localRuntimeException) { 
    } catch (SQLException localSQLException) { 
 }

下面是一个加载Driver、注册、注销、重新注册的代码示例:

public static void defaultDriver(){ 
    try { 
           
        String url = "jdbc:oracle:thin:@127.0.0.1:1521:xe"; 
           
        //1.将Driver加载到内存中,然后执行其static静态代码,创建一个OracleDriver实例注册到DriverManager中 
        Driver dd = (Driver)Class.forName("oracle.jdbc.driver.OracleDriver").newInstance(); 
        //2.取出对应的oracle 驱动Driver 
        Driver driver  = DriverManager.getDriver(url); 
        System.out.println("加载类后,获取Driver对象:"+driver); 
           
        //3. 将driver从DriverManager中注销掉 
        DriverManager.deregisterDriver(driver); 
           
        //4.此时DriverManager中已经没有了驱动Driver实例,将创建的dd注册到DriverManager中 
        DriverManager.registerDriver(dd); 
           
        //5.重新通过url从DriverManager中取Driver 
        driver  = DriverManager.getDriver(url); 
                         
        System.out.println("注销掉静态创建的Driver后,重新注册的Driver:    "+driver); 
        System.out.println("driver和dd是否是同一对象:" +(driver==dd)); 
    } catch (Exception e) { 
        System.out.println("加载Oracle类失败!"); 
        e.printStackTrace(); 
    } finally{ 
           
    } 
} 

以上其实就是早期我们加载JDBC时候的方式。但是这种从上面的代码上也可以看出,假如说我要加载不同的Driver,那么我要去加载不同的
url,来实例化不同的Driver,这样的话会造成代码的冗余,或者说代码之间的耦合性太强(代码要使用的Driver和和Driver的实现耦合到一起),
还有就是如上面写的类似于“oracle.jdbc.driver.OracleDriver”这样的一堆的字符串,所以,之后就出现了利用破坏双亲委派这种机制来加载Driver的方式。

二、JDBC现在加载驱动的方式

接下来看java是如何破坏双亲委派来加载 Driver的,看源码,首先要一个入口:

public static void main(String[] args) throws SQLException {
        String url = "jdbc:mysql://127.0.0.1:3306/test?characterEncoding=utf-8&useSSL=false";
        String username = "root";
        String password = "Ys0120.";
        Connection conn = null;
        try {
            conn = DriverManager.getConnection(url, username, password);
            PreparedStatement statement = conn.prepareStatement("select * from user where id = ?");
            statement.setLong(1,1);
            ResultSet set = statement.executeQuery();
            while(set.next()){
                System.out.println(set.getString("name"));
            }
            conn.close();
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            if(conn!=null){
                try {
                    conn.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    }

代码在执行DriverManager.getConnection(url, username, password); 首先回去实例化DriverManager,而DriverManager是整个加载的核心
DriverManager里面有一个静态的代码块,在实例化的时候会进行初始化执行。

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


loadInitialDrivers();整个方法里面最核心也是执行加载的核心的两行代码如下:


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

下面的load方法重点在于ClassLoader cl = Thread.currentThread().getContextClassLoader();

线程上下文件类加载器(Thread Context ClassLoader)。这个类加载器可以通过java.lang.Thread类的setContextClassLoader()方法进行设置,如果创建线程时还未设置,它将会从父线程中继承一个;如果在应用程序的全局范围内都没有设置过,那么这个类加载器默认就是应用程序类加载器。

public static <S> ServiceLoader<S> load(Class<S> service) {

        //因为出入的service 是  java.sql.Driver,它是一个接口,jdk支持了 spi 这种提供接口的方式
        //供调用者去实现它的接口,具体调用者去调用哪个实现,Driver不管。
        //因为当前service  是一个第三方的类或者说非java自己的类,所以JVM 是无法调用自己的
        //类加载器去加载这个Driver类的,所以它需要一个appClassLoader去加载,所以它用了
        //TCCL去获取一个 aapClassLoader,然后去执行实例化。
        // spi 指的是(service  provide interface),第三方可以以jar 的形式提供一个接口类,
        //调用者 可以根据自己的业务去实现。
        ClassLoader cl = Thread.currentThread().getContextClassLoader();
        return ServiceLoader.load(service, cl);
    }

下面是具体的实例化的代码,它的底层其实是用了Class.forName去进行实例化。至此,java在加载Driver
的时候是怎么去破坏双亲模型的给说完了,剩下的代码基本上不属于破坏双亲模型的范畴。

 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();          // This cannot happen
        }
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,240评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,328评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,182评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,121评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,135评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,093评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,013评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,854评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,295评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,513评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,678评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,398评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,989评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,636评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,801评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,657评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,558评论 2 352

推荐阅读更多精彩内容