美文网首页
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加载时是怎么破坏双亲委派的、它是怎么实现的

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