JDBC 是如何破坏双亲委派机制的?

我看了好多的博客,但是说的都很笼统。但是大致意思如下,
DriverManager 这个类 存在在 rt.jar里面由启动类加载器加载,而它里面需要用到外部引入的jar,比如说 mysql-connect.jar里面的Driver,于是DriverManager这个类不得不使用 Thread ContextClassLoader 去加载外部的具体实现.
(由这个星号标识的部分是我非常不理解的)
我仔细也试着去读代码,发现跟他们说的不一致.
我的疑问如下:
1.
Class.forName("com.mysql...");
这句话加载驱动,使用了那种类型的加载器,据我的理解应该是应用类加载器. 对吗?
2.
在mysql的Driver实现类中,使用static静态代码块,调用了DriverManager.registerDriver(new Driver()).
然后DriverManager中就持有了这个Driver,并没有发现.DriverManager 通过 ContextClassLoader去加载这个Driver的代码.
虽然DriverManager.getConnection()方法里面由一段这样的代码

synchronized(DriverManager.class) {

if (callerCL == null) {
    callerCL = Thread.currentThread().getContextClassLoader();
}

}

但是它用的driver还是register的driver,并不是由callerCL加载的.

阅读 4.8k
1 个回答

上面使用Class.forName的方式注册驱动是比较古老的方式拉,新的DriverManager中会通过SPI机制自动加载驱动,只要classpath中有相应的驱动包即可。
在新的版本中,不需要显式的加载驱动,直接DriverManager.getConnection即可。
新的DriverManager加载驱动的过程是在loadInitialDrivers方法中进行的。

    //加载驱动类有两种方式,
    //1种是通过jdbc.drivers设置驱动类的位置,然后通过SystemClassLoader
    //另外一种通过spi机制加载
    private static void loadInitialDrivers() {
        String drivers;
        try {
            drivers = AccessController.doPrivileged(new PrivilegedAction<String>() {
                public String run() {
                    return System.getProperty("jdbc.drivers");
                }
            });
        } catch (Exception ex) {
            drivers = null;
        }
        
        AccessController.doPrivileged(new PrivilegedAction<Void>() {
            public Void run() {
                //在这里会通过Thread.currentThread().getContextClassLoader()加载
                ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
                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;
        }
        String[] driversList = drivers.split(":");
        println("number of Drivers:" + driversList.length);
        for (String aDriver : driversList) {
            try {
                println("DriverManager.Initialize: loading " + aDriver);
                //这里是通过SystemLoader加载
                Class.forName(aDriver, true,
                        ClassLoader.getSystemClassLoader());
            } catch (Exception ex) {
                println("DriverManager.Initialize: load failed: " + ex);
            }
        }
    }

上面的方法知识加载驱动类,具体的注册逻辑在Driver类中,可查看Driver接口的说明:

/**
 * <P>When a Driver class is loaded, it should create an instance of
 * itself and register it with the DriverManager. This means that a
 * user can load and register a driver by calling:
 * <p>
 * {@code Class.forName("foo.bah.Driver")}
 * <p>
 **/
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题