JDBC(二)、注册驱动三种实现原理

类的加载时机

  • new对象
  • 加载子类
  • 通过类中的静态成员
  • 通过反射

使用new 对象的方式加载类的不足:

  • 属于编译器加载,如果编译期键4间该类不存在,则直接报编译错误,强依赖
  • 导致Driver对象创建了两遍,效率较低。

rs.next()

指向下一行

rs.getObject

获取单元格

创建和释放的顺序相反

最后创建的先释放

为什么需要释放

JDBC实际上是Socket连接,开端口号,占用系统的内存,个数是有限的

关闭连接非常重要,如果上线时不关闭连接,表现非常明显
尽量晚的建立连接,尽量早的释放连接,减少占用时间。

三种方法

  • 1.System.setProperty
System.setProperty("jdbc.drivers","com.mysql.jdbc.Driver:oracle.jdbc.driver.OracleDriver");

  • 2.registerDriver
    DriverManager.registerDriver(new com.mysql.jdbc.Driver());
  • 3.Class.forName
    Class.forName("com.mysql.jdbc.Driver");

前两种方法分析

loadInitialDrivers源码

原理loadInitialDrivers方法会取出属性判断

    private static void loadInitialDrivers() {
        String drivers;
        try {
            drivers = AccessController.doPrivileged(new PrivilegedAction<String>() {
                public String run() {
                    return System.getProperty("jdbc.drivers");
                }
            });
        } catch (Exception ex) {
            drivers = null;
        }
        // If the driver is packaged as a Service Provider, load it.
        // Get all the drivers through the classloader
        // exposed as a java.sql.Driver.class service.
        // ServiceLoader.load() replaces the sun.misc.Providers()

        AccessController.doPrivileged(new PrivilegedAction<Void>() {
            public Void run() {

                ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
                Iterator<Driver> driversIterator = loadedDrivers.iterator();

                /* Load these drivers, so that they can be instantiated.
                 * It may be the case that the driver class may not be there
                 * i.e. there may be a packaged driver with the service class
                 * as implementation of java.sql.Driver but the actual class
                 * may be missing. In that case a java.util.ServiceConfigurationError
                 * will be thrown at runtime by the VM trying to locate
                 * and load the service.
                 *
                 * Adding a try catch block to catch those runtime errors
                 * if driver not available in classpath but it's
                 * packaged as service and that service is there in classpath.
                 */
                try{
                    while(driversIterator.hasNext()) {
                        driversIterator.next();
                    }
                } catch(Throwable t) {
                // Do nothing
                }
                return null;
            }
        });

        println("DriverManager.initialize: jdbc.drivers = " + drivers);

        if (drivers == null || drivers.equals("")) {
            return;
        }
        String[] driversList = drivers.split(":");
        println("number of Drivers:" + driversList.length);
        for (String aDriver : driversList) {
            try {
                println("DriverManager.Initialize: loading " + aDriver);
                Class.forName(aDriver, true,
                        ClassLoader.getSystemClassLoader());
            } catch (Exception ex) {
                println("DriverManager.Initialize: load failed: " + ex);
            }
        }
    }

registerDriver

    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);

    }    


deregisterDriver(Driver driver) 卸载驱动源码

    @CallerSensitive
    public static synchronized void deregisterDriver(Driver driver)
        throws SQLException {
        if (driver == null) {
            return;
        }

        SecurityManager sec = System.getSecurityManager();
        if (sec != null) {
            sec.checkPermission(DEREGISTER_DRIVER_PERMISSION);
        }

        println("DriverManager.deregisterDriver: " + driver);

        DriverInfo aDriver = new DriverInfo(driver, null);
        if(registeredDrivers.contains(aDriver)) {
            if (isDriverAllowed(driver, Reflection.getCallerClass())) {
                DriverInfo di = registeredDrivers.get(registeredDrivers.indexOf(aDriver));
                 // If a DriverAction was specified, Call it to notify the
                 // driver that it has been deregistered
                 if(di.action() != null) {
                     di.action().deregister();
                 }
                 registeredDrivers.remove(aDriver);
            } else {
                // If the caller does not have permission to load the driver then
                // throw a SecurityException.
                throw new SecurityException();
            }
        } else {
            println("    couldn't find driver to unload");
        }
    }

第三种实现

static块中将驱动注册进ManagerDriver,所以前两种方式实际上

package com.mysql.jdbc;

import java.sql.DriverManager;
import java.sql.SQLException;

public class Driver extends NonRegisteringDriver implements java.sql.Driver {
    public Driver() throws SQLException {
    }

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

其他:

driverinfo封装每一个传入的driver的信息

private final static CopyOnWriteArrayList<DriverInfo> registeredDrivers


 registeredDrivers.addIfAbsent(new DriverInfo(driver, da));
 
如果没有就添加进CopyOnWriteArrayList,

CopyOnWriteArrayList的特点:

  • 实现了List接口
  • 内部持有一个ReentrantLock lock = new ReentrantLock();
  • 底层是用volatile transient声明的数组 array
  • 读写分离,写时复制出一个新的数组,完成插入、修改或者移除操作后将新数组赋值给array

写时复制:
https://blog.csdn.net/weixin_39554266/article/details/82835478

三种比较

  • 后两种都是字符串,反射得到类,去掉jar包也能通过编译。
  • 第一种,new对象依赖于jar包,不能通过编译。(更换数据库需要改代码)
原文地址:https://www.cnblogs.com/biturd/p/12623150.html