彻底搞明白JAVA中JDBC连接

一开始我们在学习JDBC的时候,老师就教我们了以下几步来建立JDBC连接.

  1. Class.forName到底干了什么

首先Class.forName来加载注册我们的mysql驱动,执行完这个Class.forName, 到底我们的类里,哪些方法会被执行呢.我们写个类来测试一下,测试类如下

最后我们使用Class.forName(“org.linuxsogood.boot.jdbc.TestInit”)测试的结果是,只有静态代码块里的代码被执行了. 

我们再看一下myql的Driver类里是怎么写的

mysql的Driver类,其实还是调用了DriverManager的注册驱动方法,new了一个自己的类.仅此. 所以我们在使用JDBC连接MySQL的时候,可以完全不用Class.forName, 我们可以直接使用DriverManager的registerDriver方法手动来注册,测试下来,结果也确实是一样的.

然后我们再重点关注一下DriverManager类

2. 为什么我不写Class.forName, 直接使用DriverManager.getConnection也可以获取MySQL连接?

我测试着把Class.forName给注释掉,发现程序依然能正常运行,这和我们初学的时候,老师教我们的,好像有点违背,不是说一定要按这个步骤吗? 太奇怪了.那下面我们就重点关注一下DriverManager这个类,到底是在搞什么飞机.

首先看一下源码上的注释

重点读一下以上说明,大概意思是说:

DriverManager会先读取系统环境变量jdbc.drivers属性, 我们可以通过设置这个属性的值,来达到让DriverManager来帮我们注册数据库驱动的目的,如果有多个数据库驱动,多个之间使用:号分隔

DriverManager的getConnection方法做了增强, 自从JDBC 4.0开始,就不需要再使用Class.forName的方式来注册数据库驱动了, 使用的是一java里面的Service Loader的机制, Service Loader机制要求在classpath下面有一个META-INF/services的目录,并且在这个目录下,建立一个接口全路径名的文件,比如java.sql.Driver的文件,内容写上

这样在使用ServiceLoader.load(Driver.class)的时候,文件里面这两个类,会被系统加载,并且延迟执行.

和Class.forName不同的就是这个是延迟执行的,只有当你把这个类取出来的时候,里面的静态方法,才会被执行.

我们继续看getConnection方法,到底是怎么搞的

我们发现他是从registeredDrivers这个集合中去找对应的驱动.是根据你数据库连接的URL来找的.那这个集合是啥时候被初始化的呢.我们代码只写了一个getConnection方法,别的没写,应该不会跑出DriverManager这个类,当我们调用这个类的方法的时候,我们知道,静态代码块应该会被调用,看一下这个类的静态代码块.

跟踪loadInitialDrivers方法

感觉回到了这个类上的注释上面, 首先读取系统变量jdbc.drivers, 其次我们看到了一个ServiceLoader.load方法,load了Driver.class类. 这个就是前面注释上讲过的ServiceLoader, 它会将实现Driver接口的classPath中找META-INF/service中的java.sql.Driver文件中的驱动,并且加载他们.为什么是META-INF/service这个路径,这是因为ServiceLoader的实现机制就是这样的,去在这样一个路径下寻找你的接口的全路径名的文件,并加载其中的每一行. 因为java对JDBC的驱动都必须实现java.sql.Driver接口,所以就是找这个文件.然后依次循环. 如果没有后面的循环,我们的类只是被加载进内存,并不会触发里面的静态代码块的方法.这是因为ServiceLoader的懒加载机制.

3.模拟java.sql.Driver自己写一个使用ServiceLoader的例子

首先定义一个接口

然后定义实现类

再定义一个

在classpath目录下,我这里是maven项目,就在resources目录下,创建META-INF/services目录

然后建立一个org.linuxsogood.boot.serviceloader.IService名字的文件,里面写入以下两行

写一个测试类来测试

我们发现,HDFSServiceImpl里面的静态代码块被执行了. 如果云掉下面的遍历操作,加载能成功,但是静态代码块不会被调用.这也就是为什么,MySQL的Driver里面,初始化要在静态代码块里面来做了

发表评论

电子邮件地址不会被公开。 必填项已用*标注

Captcha Code