2. 注册 Driver

发布时间:2024年01月20日

本文有三个点:

  1. 运用 SPI 机制
  2. Java 类加载机制之 JDBC 打破双亲委派
  3. Driver 的注册
SPI 机制加载 rt.Driver的实现类
  1. Jdk 源码包的 rt.jar定义接口 Driver

  2. 第三方 jar 包实现接口,同时为了可以被 SPI 机制发现(在META-INF/services文件中,以接口的全限定名来命名文件名,文件里面写该接口的实现)。

    ?image?

  3. SPI 机制将所有第三方 jar 包实现类加载到 Jvm 中

  4. 开发人员根据自己的逻辑合理选择 jar 包实现。

参考源码分析流程

在执行DriverManager.registerDriver(new Driver());?前,jvm 类加载系统会先将 DriverManager.class字节码文件加载到 jvm 中。并执行静态方法loadInitialDrivers();?

  1. 获取系统属性“jdbc.drivers”的值drivers
  2. ?ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);? 这里用到了 SPI 机制。
  3. ?Iterator<Driver> driversIterator = loadedDrivers.iterator();?
  4. 将 drivers 根据分隔符“:”分割为 driverList,逐个进行 Class.forName(driverName);
private static void loadInitialDrivers() {
        String drivers;
        try {
            drivers = AccessController.doPrivileged(new PrivilegedAction<String>() {
                public String run() {
                    return System.getProperty("jdbc.drivers");   //null
                }
            });
        }  
        AccessController.doPrivileged(new PrivilegedAction<Void>() {
            public Void run() { 
                ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);    //loadedDrivers  = java.sql.Driver接口
                Iterator<Driver> driversIterator = loadedDrivers.iterator(); 
                try{
                    // 在 hasNext()中找到待实例化的全类名,在 next()中实例化该类。
                    while(driversIterator.hasNext()) {   
                        driversIterator.next();                
                    }
                } 
                return null;
            }
        });  
    }
打破双亲委派

父加载器委托子加载器加载,打破了双亲委派机制。

  1. 由于 rt 包的类加载是 bootstrap classloader
  2. rt 包中DriverManager在loadInitialDrivers中,使用到了 ServiceLoader 触发 Driver.class的加载
  3. 先加载 java.sql.Driver,此时使用到的是 bootstrap classloader
  4. 然后ClassLoader cl = Thread.currentThread().getContextClassLoader();此时的上下文加载器为 App Classloader。

这种父类加载器委托子类加载的行为边打破了双亲委派机制

?image?

驱动注册

其实我们可以直接通过这种方式进行驱动注册。

DriverManager.getConnection(String url,Properties info,Class<?> caller);

而不是下文的这种调用方式,下文这种 Class.forName注册驱动其实是可以省略的。看代码:

 public static void main(String[] args) {
    Connection conn = null;
    Statement stmt = null;
    // 2. 加载 JDBC 驱动
    Class clazz= Class.forName("com.mysql.jdbc.Driver");
    Driver driver = (Driver) clazz.newInstance();
    // 3. 注册驱动
    DriverManager.registerDriver(driver);
    // 4. 打开链接
    System.out.println("连接数据库...");
    conn = (Connection)DriverManager.getConnection(DB_URL,USER,PASS);   
}
  1. 调用 Class.forName(driverName);注册驱动。这一部是非必须的,这样写不会打破双亲委派机制。可以直接第三步

    1. 加载全限定名称为 driverName 的类
    2. 类加载过程中的初始化阶段,会执行静态代码块DriverManager.registerDriver(new Driver());??
  2. 在执行DriverManager.registerDriver(new Driver());?前,jvm 类加载系统会先将 DriverManager.class字节码文件加载到 jvm 中。并执行静态方法loadInitialDrivers();?SPI 机制 加载配置的 jdbc.drivers 属性中的多个驱动

  3. DriverManager.getConnection(String url,Properties info,Class<?> caller); 获取连接??

?

文章来源:https://blog.csdn.net/baijunzhijiang_01/article/details/135709490
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。