Mybatis源码阅读之二 JDBC驱动加载过程与Mybatis使用JDBC

1,093 阅读2分钟

JDBC驱动加载过程

初学的时候JDBC加载通常使用Class.forName("com.mysql.jdbc.Driver") 来加载驱动,执行Driver实现类的静态代码块,触发注册到DriverManager中。

新版的Mysql驱动加载

新版的Mysql驱动,无需你做任何事情,只需要把驱动jar加载到classpath中去即可:这个是Jdbc 4.0之后提供的SPI机制

 @Test
    public void testJdbcDriver() throws SQLException {
        Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:13306/mybatis?user=root&password=123456"); // 直接获取连接
        
    }

以上的情况只是在classpath是系统类加载器环境才生效。也就是自动加载

通常使用mysql都跟Web结合起来,比如Tomcat, Servlet容器,都会有他们自己的类加载器,Tomcat应用类加载器是ParallelWebappClassLoader,所以您在这种环境中使用上述代码,肯定会报空指针或者classnotfoundexception,因为驱动并没有注册到DriverManager,因为Tomcat启动的时候,在启动类加载器这一步的时候,找不到mysql驱动。那么就需要手动加载了。

手动加载

	1. new com.mysql.cj.jdbc.Driver();
	2. Class.forName("com.mysql.cj.jdbc.Driver", true, classLoader)

因为mysql驱动并不是存放在tomcat的classpath中,而是在web应用中的lib目录下。虽然你存放在tomcat lib目录下也是可以的,但是最好不要那么样子做,不方便。

Mybatis 源码探究

在Mybatis的org.apache.ibatis.datasource.unpooled.UnpooledDataSource#initializeDriver中,此方法便是加载mysql驱动的

private synchronized void initializeDriver() throws SQLException {
	  //这里便是大家熟悉的初学JDBC时的那几句话了 Class.forName newInstance()
    if (!registeredDrivers.containsKey(driver)) {
      Class<?> driverType;
      try {
        if (driverClassLoader != null) {
          driverType = Class.forName(driver, true, driverClassLoader); 1.
        } else {
          driverType = Resources.classForName(driver); 
        }
        // DriverManager requires the driver to be loaded via the system ClassLoader.
        // http://www.kfu.com/~nsayer/Java/dyn-jdbc.html
        Driver driverInstance = (Driver)driverType.newInstance(); 2. 
        DriverManager.registerDriver(new DriverProxy(driverInstance)); 3.
        registeredDrivers.put(driver, driverInstance);
      } catch (Exception e) {
        throw new SQLException("Error setting driver on UnpooledDataSource. Cause: " + e);
      }
    }
  }

解释: 1.首先driverClassLoader是Caller类的classLoader,如果在自定义类加载器的情况下,到最后注册的时候肯定报错的 2.new一个Driver,因为在自定义的类加载器中走的不是SPI机制,所以需要自己动手,丰衣足食, 作用只是触发Driver的static块 3.DriverManager#registerDriver静态方法,触发静态初始化块,并注册Driver

Mysql 驱动Driver代码

static {
        try {
            java.sql.DriverManager.registerDriver(new Driver());
        } catch (SQLException E) {
            throw new RuntimeException("Can't register driver!");
        }
    }

反正无论如何加载,只要执行了java.sql.DriverManager.registerDriver(new Driver());这行,那么就可以使用了。通常统一使用java.sql.DriverManager#getConnection(java.lang.String, java.util.Properties, java.lang.Class<?>)来获取连接。 仔细看一下,其实mybatis内置的连接池通过driver地址进行去重判断,再进而初始化,并保存在map中,所以我们经常用别的连接池,比如Hikari、Druid等。

关于SPI机制,请看下图

SPI机制

  • 关于SPI其实就是帮我们自动加载相关服务而已,就算没有SPI,那么我们可以手动加载。
  • 至于线程上下文类加载器的作用就是用来搜索类的,是因为在当前的类加载器中是没有加载指定的类,so请线程上下文加载器帮忙。