1. 目标
- 探究一下jdbc获取连接过程的具体实现过程
- 对jdbc和数据库具体交互过程有更深刻的认识
- 理解Class.forName加载驱动方式的出现原因
2. 范围
- jdbc的驱动选择mysql的驱动
- jdbc驱动程序的版本 8.0.18
3. jdbc原理
- jdk中包含了jdbc关于数据库操作的API,主要在java.sql,javax.sql包中
- 每个具体的数据库(比如MySQL)都会有关于jdbc的驱动程序
- 驱动程序实现相关api的接口,去与数据库进行通信
- 驱动程序实现了MySQL通信协议,MySQL通信协议是一个基于TCP的应用层协议
4. 获取连接的范式代码
获取数据库的连接的代码可以参考jdk和mysql的文档
//jdk文档中的关于查询的一段写法: https://docs.oracle.com/javase/tutorial/jdbc/overview/index.html#relational//连接方式的写法,可以看MySQL文档中的一段写法
最终写出来这种形式
public static void main(String[] args) throws Exception{
Connection connection = DriverManager.getConnection(
"jdbc:mysql://localhost/test",
"root","password");
System.out.println(connection);
}
大家有可能见过这种写法
public static void main(String[] args) throws Exception{
Class.forName("com.mysql.jdbc.Driver");
Connection connection = DriverManager.getConnection(
"jdbc:mysql://localhost/test",
"root","password");
System.out.println(connection);
}
这种写法的区别就是获取连接之前多了驱动类的类加载过程,后面解释为什么会出现这种写法
5. 主逻辑过程分析
(1)使用DirverManager这个类的时候,会先执行类的代码块调用加载驱动的方法 loadInitialDirvers方法
(2)loadInitialDrivers 方法会引发驱动程序jar包中驱动类的加载,需要驱动支持spi的方式
(3)驱动程序将自己添加到已经注册的驱动列表中
(4)getConnection方法,会遍历驱动寻找到的适合的驱动程序进行工作
这里有几个细节不是很清楚,可以继续细化的点:
- ServiceLoader的机制?
- 驱动实现的Driver具体如何获取连接的?
6. 主逻辑代码分析
7. ServiceLoader机制
ServiceLoader 机制简单描述:
几点说明:
- 遵循注册申明方式,调用方完全依赖于接口,达到了解耦的目的
- 拆分到不同的jar中,使得可以独立编译发布
主代码逻辑中描述的驱动注册过程是通过这种机制来实现的。
8. Dirver获取连接
先看一个简单的例子:
public static void main(String[] args) throws Exception{
//和mysql建立tcp连接
Socket socket = new Socket("127.0.0.1", 3306);
InputStream inputStream = socket.getInputStream();
//读取连接后的tcp响应数据
byte[] buffer = new byte[1024];
int len = 0;
while (( len = inputStream.read(buffer)) != -1){
int index = 0;
int currentLen = 0;
for(byte a : buffer){
System.out.print(Integer.toHexString(a) + " ");
index ++ ;
if(index % 16 == 0){
index = 0;
System.out.println();
}
currentLen ++;
if(currentLen == len - 1){
break;
}
}
}
}
运行程序后得到响应结果:
同时wireshark抓包结果:
分析:
程序的响应结果和我们程序拿到的结果是一致的,在建立tcp连接后,mysql服务会主动发送一条消息,信息中包含了mysql的版本信息、密码校验方式等,这个消息的解析方式,需要用到mysql协议,在mysql的官方文档中有具体说明,按照这个规则进行解析就可以了(不用去具体看怎么解析,了解原理即可)
MySQL :: MySQL Internals Manual :: 14.2.5 Connection Phase Packets
现在从驱动源码中可以跟踪到和我们类似的socket的代码,可以确定起获取连接的原理其实和我们demo里面的工作原理类似了,只不过有很多细节要处理(不是我们关注的重点,除非我们自己想实现一个驱动程序),最终跟踪到如下位置代码,其实不仅是连接,后面的所有的通信都是基于这个socket也是基于这个时候建立的这个socket进行通信的,每次调用getConnection方法都会建立一个这样的socket连接
9. Class.forName的解释
新版本的mysql驱动是符合ServiceLoader的机制要求,所以可以自动把驱动注册,但是以前版本的驱动程序中就不符合这种ServiceLoader的机制要求,所以要想驱动注册完成,需要强制触发类加载,从而触发类中static块代码的执行。这里我们选择5.0.8 版本驱动就发现有这个问题,如果没有使用Class.forName 去触发就会报错了。
10.总结
- MySQL通过jdbc与客户端通信,底层是tcp连接+MySQL协议
- MySQL通过jdbc与客户端通信,在java层面也是通过我们的socket编程来实现的
- MySQL的Class.forName是一个历遗留问题,后来应用了ServiceLoader机制可以不用这么写了