美文网首页
JDBC Driver加载时是怎么破坏双亲委派的、它是怎么实现的

JDBC Driver加载时是怎么破坏双亲委派的、它是怎么实现的

作者: Easy的幸福 | 来源:发表于2019-08-28 15:21 被阅读0次

一、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
        }

相关文章

  • JDBC Driver加载时是怎么破坏双亲委派的、它是怎么实现的

    一、JDBC之前加载驱动的方式 在说破坏双亲委派之前,先看下之前是怎么加载Driver的。在刚开始的时候JDBC在...

  • 2018-07面试题目

    1. JVM的类加载机制 主要是双亲委派模型的东西。它是什么、怎么工作的、为什么要有它,以及打破双亲委派模型的...

  • JVM类加载过程详细分析

    双亲委派加载模型 为什么需要双亲委派加载模型 主要是为了安全,避免用户恶意加载破坏JVM正常运行的字节码文件,比如...

  • 类加载器与双亲委派机制

    前言 什么是类加载器?类加载器有哪些?双亲委派机制是怎么样的?什么时候需要打破双亲委派?如何打破? 以上内容网上有...

  • SPI的ClassLoader问题

    问题 为什么说spi服务机制破坏了双亲委派模型? 双亲委派机制 启动类加载器(Bootstrap ClassLoa...

  • java 类加载 双亲委派 破坏双亲委派

    加载:在硬盘上查找并通过IO读入字节码文件至JVM虚拟机方法区,同事在堆中创建Class对象 验证:校验字节码文件...

  • 深入理解Tomcat(五)类加载机制

    前言 我们知道,Java默认的类加载机制是通过双亲委派模型来实现的。而Tomcat实现的方式又和双亲委派模型有所区...

  • 3.手写自己的java类加载器

    类的加载过程?何为双亲委派机制?为啥这么设计?实现一个自己的类加载器?如何打破双亲委派机制? 1.类加载器 jar...

  • ClassLoader重点梳理

    类加载器 对类加载器的学习重点要掌握以下几点: 双亲委派模型的概念 双亲委派模型的实现原理 类加载器的工作原理 如...

  • 安卓classloader浅析

    classloader 是采用双亲委派的方式加载所需要的类。 双亲委派:从classloader的源码分析,在加载...

网友评论

      本文标题:JDBC Driver加载时是怎么破坏双亲委派的、它是怎么实现的

      本文链接:https://www.haomeiwen.com/subject/mfxmectx.html