JDBC连接数据库操作代码
// 加载数据库驱动
Class.forName("com.mysql.cj.jdbc.Driver");
// 建立连接
conn = DriverManager.getConnection(url, user, password);
核心流程比较简单,无非是加载数据库驱动、获取数据库连接、创建Statement
对象,之后执行相应的数据库操作
在JDBC4以后不用显示的使用 Class.forName加载mysql的驱动,而是系统自动发现识别并注册数据库驱动。
这是因为,在JDBC4以采用了SPI机制
SPI(Service Provider Interface)是Java平台内置的一种服务提供发现机制,它允许应用程序动态地加载和使用第三方提供的服务实现,而无需在代码中显式引用这些实现类。
SPI机制的核心思想是解耦,即将接口和其具体实现分离,这种机制在模块化设计中非常重要,因为它提高了框架的扩展性和可维护性。在Java中,SPI机制通常在Classpath路径下的META_INF/services文件夹中实现,其中以接口的全限定名命名文件,文件内容为接口的实现类的全限定名。这些实现类可以通过ServiceLoader类加载并实例化,ServiceLoader使用迭代器模式来加载实现类。
在我们引入的数据库连接mysql-connector-java.jar包中META_INF/services目录下,存在一个以java.sql.Driver命名的文件,java.sql.Driver为Driver接口的全限定类名,文件中定义的内容com.mysql.cj.jdbc.Driver为对应MySQL数据库驱动实现类的全限定类名:
这里通过调用Thread.currentThread().getContextClassLoader()方法,获取线程上下文的类加载器,使用线程上下文类加载器完成对MySQL驱动的加载
public class DriverManager {
/**
* 加载初始JDBC驱动程序
*/
static {
loadInitialDrivers();
}
private static void loadInitialDrivers() {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
// 使用ServiceLoader类加载并实例化Driver
ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
// 迭代器遍历加载
Iterator<Driver> driversIterator = loadedDrivers.iterator();
try{
while(driversIterator.hasNext()) {
driversIterator.next();
}
} catch(Throwable t) {
}
return null;
}
});
}
public static <S> ServiceLoader<S> load(Class<S> service) {
// 获取当前线程上下文的类加载器ClassLoader
ClassLoader cl = Thread.currentThread().getContextClassLoader();
return ServiceLoader.load(service, cl);
}
}
public static void main(String[] args) {
// 打印线程上下文类加载器
System.out.println(Thread.currentThread().getContextClassLoader());
}
结果输出
sun.misc.Launcher$AppClassLoader@18b4aac2
是的,正是我们的应用程序类加载器AppClassLoader。
简要回顾一下整体流程:
启动类加载器优先加载了DriverManager驱动管理类
DriverManager通过SPI机制加载jar包中对应的MySQL驱动
SPI机制通过线程上下文类加载器(AppClassLoader)完成加载MySQL驱动
看到这,我们发现DriverManager采用的SPI机制打破了双亲委派,其驱动的实现类由启动类加载器委托给应用程序类加载器去完成加载的。
但关于SPI机制是否真正打破了双亲委派,在网上似乎存在不同的理解,在这里结合自己的看法,总结一下:
从类加载的结果来说:并没有违背双亲委派,针对这里的两个jar包,rt.jar包中DriverManager依然由启动类加载器执行的加载,用户类路径classpath下mysql-connector-java.jar包中MySQL驱动则正常通过应用程序类加载器完成加载。
从类加载的过程来说:确实是违背了双亲委派机制,因为在执行过程中抛弃双亲委派加载流程,启动类加载器Bootstrap向下委托给应用程序类加载器AppClassLoader去完成加载的,逆向的委托了类加载器。
参考:https://blog.csdn.net/mm1274889792/article/details/138443721