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,那么我们可以手动加载。
- 至于线程上下文类加载器的作用就是用来搜索类的,是因为在当前的类加载器中是没有加载指定的类,so请线程上下文加载器帮忙。