源码分析JDBC加载数据库驱动

通过源码分析JDBC加载数据库驱动

本文是对类加载器学习总结!!!

一、问题引入

1、我们平时如果在项目中需要使用到数据库(以MySQL数据库为例)首先我们需要将MySQL数据库的驱动放在class path下
2、在需要使用数据库的地方使用 Class.forName(“com.mysql.jdbc.Driver”);来加载数据库驱动
3、使用DriverManager.getConnection(“url”,“userName”,“password”);来获取数据库连接
4、然后对数据库进行增删改查操作
引用一篇说的很好的博客的介绍如下:https://blog.csdn.net/tianwang1101/article/details/76078964

一、重要的接口:
  1.public interface Driver 每个驱动程序类必须实现的接口。Java SQL 框架允许多个数据库驱动程序。每个驱动程序都应该提供一个实现 Driver 接口的类。DriverManager 会试着加载尽可能多的它可以找到的驱动程序,然后,对于任何给定连接请求,它会让每个驱动程序依次试着连接到目标 URL。强烈建议每个 Driver 类应该是小型的并且是单独的,这样就可以在不必引入大量支持代码的情况下加载和查询 Driver 类。在加载某一 Driver 类时,它应该创建自己的实例并向 DriverManager 注册该实例。这意味着用户可以通过调用以下程序加载和注册一个驱动程序 Class.forName(“foo.bah.Driver”)。例如:MYSQL驱动 com.mysql.jdbc.Driver
  2.public interface Connection 与特定数据库的连接(会话)。在连接上下文中执行 SQL 语句并返回结果。
  3.public interface Statement 用于执行静态 SQL 语句并返回它所生成结果的对象。
  4.public interface PreparedStatement 表示预编译的 SQL 语句的对象。SQL 语句被预编译并存储在 PreparedStatement 对象中。然后可以使用此对象多次高效地执行该语句。
  以上接口都是由JDK进行提供,由具体的数据库厂商进行实现
  二、驱动的加载方式
  1.最常用的是使用 Class.forName(“com.mysql.jdbc.Driver”);方式。这行代码只是使用加载当前的类加载去加载具体的数据库驱动,不要小看这简单的这一行代码。在Driver类中的static域中把当前驱动注册到DriverManager中。

二、Java Doc介绍

从JDBC 2.0以后,即使我们不写Class.forName(“com.mysql.jdbc.Driver”);语句也仍然可以加载数据库驱动,建立数据库连接和正常使用数据库,原因如下:

Java Doc描述如下:

Applications no longer need to explicitly load JDBC drivers using Class.forName(). Existing programs which currently load JDBC drivers using Class.forName() will continue to work without modification.
When the method getConnection is called, the DriverManager will attempt to locate a suitable driver from amongst those loaded at initialization and those loaded explicitly using the same classloader as the current applet or application.

翻译如下:

应用程序不再需要使用Class.forName()显式加载JDBC驱动程序。当前使用Class.forName()加载JDBC驱动程序的现有程序将继续运行,而无需进行修改。
调用方法getConnection时,DriverManager将尝试从初始化时加载的驱动程序和使用与当前applet或应用程序相同的类加载器显式加载的驱动程序中找到合适的驱动程序。

三、源码分析

从如下两行代码对数据库驱动加载做一个粗略的分析,看一看为什么我们不需要显示的使用Class.forName(“com.mysql.jdbc.Driver”);语句也能将数据库驱动加载进来。既然不使用Class.forName()加载驱动,那么只剩一条语句了,在DriverManager.getConnection(“url”,“userName”,“password”);语句中一定有加载数据库驱动的过程!
示例代码如下:

public class DriverForNameDemo {

    public static void main(String[] args) throws Exception {
        //Class.forName("com.mysql.jdbc.Driver");
        DriverManager.getConnection("url","userName","password");
    }
}
①、进入DriverManager类

首先在我们的DriverForNameDemo 类中调用了DriverManager类的getConnection()方法,造成了对DriverManager类的主动使用,此时便会加载和初始化DriverManager类,执行该类的静态代码块!跟踪进入DriverManager中,源码如下(去除掉注释和用不到的方法和属性),可以看到在该类的静态方法中调用了loadInitialDrivers()方法!

public class DriverManager {

    private DriverManager(){}
    /**
     * Load the initial JDBC drivers by checking the System property
     * jdbc.properties and then use the {@code ServiceLoader} mechanism
     */
    static {
        loadInitialDrivers();
        println("JDBC DriverManager initialized");
    }
  }
②、跟踪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;
        }
        AccessController.doPrivileged(new PrivilegedAction<Void>() {
            public Void run() {
				//从Classpath下META-INF/services目录去加载配置文件,并将配置文件中的数据库驱动加载到jvm不初始化
                ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
                Iterator<Driver> driversIterator = loadedDrivers.iterator();
                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);
                //使用系统类加载器将数据库驱动加载到jvm中,并且初始化每一个数据库驱动(参数为true)
                Class.forName(aDriver, true,
                        ClassLoader.getSystemClassLoader());
            } catch (Exception ex) {
                println("DriverManager.Initialize: load failed: " + ex);
            }
        }
    }

重要的代码片段说明:
1、从系统中通过“jdbc.drivers”取得数据库驱动,drivers是一个个驱动的名称

		String drivers;
        try {
            drivers = AccessController.doPrivileged(new PrivilegedAction<String>() {
                public String run() {
                    return System.getProperty("jdbc.drivers");
                }
            });
        } catch (Exception ex) {
            drivers = null;
        }

2、如果通过System.getProperty(“jdbc.drivers”);取得的驱动名称不为空的话就使用Class.forName()对每一个驱动进行加载,如果drivers为空的话则直接return

 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);
                //使用系统类加载器将数据库驱动加载到jvm中,并且初始化每一个数据库驱动(参数为true)
                Class.forName(aDriver, true,
                        ClassLoader.getSystemClassLoader());
            } catch (Exception ex) {
                println("DriverManager.Initialize: load failed: " + ex);
            }
        }

3、通过ServiceLoader类从Classpath下的数据库jar包中寻找META-INF/services/java.sql.Driver文件,如果存在该文件,则将里面的每一行(每一行都是一个数据库驱动全类名)都读取进来,然后进行加载。关于ServiceLoader类可查看上篇ServiceLoader详解

//从Classpath下META-INF/services目录去加载配置文件,并将配置文件中的数据库驱动加载到jvm不初始化
                ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
                Iterator<Driver> driversIterator = loadedDrivers.iterator();
                try{
                    while(driversIterator.hasNext()) {
                        driversIterator.next();
                    }
                } catch(Throwable t) {
                // Do nothing
                }

这样即使我们不使用Class.forName()显示的加载数据库驱动,也可以使用数据库了。因为在我们使用到DriverManager类获取连接的时候会导致该类的初始化,就会执行该类的静态代码块,在静态代码块中使用了ServiceLoader类自动从classpath下的数据库驱动jar包中加载了数据库驱动类!!!
​?​ 缺点 使用ServiceLoader类来从classpath下加载驱动会将META-INF/services/java.sql.Driver文件中的每一个驱动都加载进来(即使我们不使用)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值