JDBC项目实践与源码解析(四)

JDBC项目实践与源码解析——加载驱动

本专栏为系列文章,如果想要系统学习JDBC,作者建议从第一篇文章,顺序读完全部文章。
文中项目源码地址:https://github.com/bethanwang/jdbc-study

通过反射机制,生成驱动类的实例,即生成com.mysql.cj.jdbc.Driver类的对象,并将驱动类实例注册到java.sql.DriverManager进行管理。

//驱动类
private static final String DRIVER_CLASS = "com.mysql.cj.jdbc.Driver";
//加载驱动
Class.forName(DRIVER_CLASS);

关于反射和类的加载机制本文不展开讲解。

源码解析:

​ 先来看一下代码中private static final String DRIVER_CLASS = "com.mysql.cj.jdbc.Driver"这一行里面com.mysql.cj.jdbc.Driver类的源码,如下:

package com.mysql.cj.jdbc;

import java.sql.SQLException;

public class Driver extends NonRegisteringDriver implements java.sql.Driver {

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

    /**
     * Construct a new driver and register it with DriverManager
     * 
     * @throws SQLException
     *             if a database error occurs.
     */
    public Driver() throws SQLException {
        // Required for Class.forName().newInstance()
    }
}

​ 当我们执行Class.forName("com.mysql.cj.jdbc.Driver")这一行代码时,会通过反射实例化com.mysql.cj.jdbc.Driver类的对象,通过查看com.mysql.cj.jdbc.Driver类的源码,我们知道在实例化其对象时会执行static代码块中的java.sql.DriverManager.registerDriver(new Driver())方法。

java.sql.DriverManager类中的源码:

package java.sql;

public class DriverManager {
  
  /*
  一个线程安全的List集合,注册后的com.mysql.cj.jdbc.Driver的对象,封装到DriverInfo的对象中,然后将DriverInfo的对象,放到registeredDrivers集合内,完成注册
  */
  private final static CopyOnWriteArrayList<DriverInfo> registeredDrivers = new CopyOnWriteArrayList<>();
  
  public static synchronized void registerDriver(java.sql.Driver driver)
    throws SQLException {
    registerDriver(driver, null);
  }
  
  public static synchronized void registerDriver(java.sql.Driver driver,
            DriverAction da)
        throws SQLException {

        /* Register the driver if it has not already been added to our list */
        if(driver != null) {
            registeredDrivers.addIfAbsent(new DriverInfo(driver, da));
        } else {
            // This is for compatibility with the original DriverManager
            throw new NullPointerException();
        }

        println("registerDriver: " + driver);

    }
  
}

class DriverInfo {

    final Driver driver;
    DriverAction da;
    DriverInfo(Driver driver, DriverAction action) {
        this.driver = driver;
        da = action;
    }
}

​ 通过java.sql.DriverManager的源码我们看到,执行registerDriver(java.sql.Driver driver)方法后,会调用registerDriver(java.sql.Driver driver, DriverAction da)方法;通过registeredDrivers.addIfAbsent(new DriverInfo(driver, da))这一行代码可以看出,创建了一个DriverInfo对象,并将com.mysql.cj.jdbc.Driver的对象封装到DriverInfo中,最后将DriverInfo对象添加到registeredDrivers集合中,完成驱动的注册。

*扩展:基于SPI隐式加载驱动:

所谓Driver隐式加载,就是Class.forName(DRIVER_CLASS);这一行代码不需要再写了。

	JDBC4.0开始,这个显式的初始化驱动的代码就可以不用写了,很多时候之所以这样写是考虑到线上兼容。
	在java 6以后,加入了SPI(Service Provider Interface)功能,SPI提供了一种JVM级别的服务发现机制,只需要在jar包中按照SPI的格式要求进行配置,JVM就会在运行时通过懒加载,帮我们找到所需的服务并加载。如果系统中没有使用到这个服务,则不会被加载,避免资源浪费。
	SPI的配置路径是:/META-INF/services/下面,我们程序中使用的”mysql-connector-java-8.0.19.jar”也添加了对应的配置,所以在我们的项目中,可以隐式加载驱动,即可以不写`Class.forName(DRIVER_CLASS);`这一行代码。

mysql-connector-java-8.0.19.jar中关于SPI的配置:
在这里插入图片描述
上一篇:系统学习JDBC——JDBC实践
下一篇:JDBC深入讲解——打开数据库连接

受作者水平限制,文中难免有不足之处,若读者阅读过程中发现问题,还望及时指正,感谢支持!
​ 作者邮箱:547317812@qq.com

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Captain-船长

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值