jdbc 获取连接分析

1. 目标

  • 探究一下jdbc获取连接过程的具体实现过程
  • 对jdbc和数据库具体交互过程有更深刻的认识
  • 理解Class.forName加载驱动方式的出现原因

2. 范围

  • jdbc的驱动选择mysql的驱动
  • jdbc驱动程序的版本 8.0.18

3. jdbc原理

  1. jdk中包含了jdbc关于数据库操作的API,主要在java.sql,javax.sql包中
  2. 每个具体的数据库(比如MySQL)都会有关于jdbc的驱动程序
  3. 驱动程序实现相关api的接口,去与数据库进行通信
  4. 驱动程序实现了MySQL通信协议,MySQL通信协议是一个基于TCP的应用层协议

4. 获取连接的范式代码

获取数据库的连接的代码可以参考jdk和mysql的文档

//jdk文档中的关于查询的一段写法:
https://docs.oracle.com/javase/tutorial/jdbc/overview/index.html#relational

//连接方式的写法,可以看MySQL文档中的一段写法

MySQL :: MySQL Connector/J 8.0 Developer Guide :: 7.1 Connecting to MySQL Using the JDBC DriverManager Interface

最终写出来这种形式

    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方法,会遍历驱动寻找到的适合的驱动程序进行工作

 这里有几个细节不是很清楚,可以继续细化的点

  1. ServiceLoader的机制?
  2. 驱动实现的Driver具体如何获取连接的?

6. 主逻辑代码分析

7. ServiceLoader机制

ServiceLoader 机制简单描述:

几点说明:

  1. 遵循注册申明方式,调用方完全依赖于接口,达到了解耦的目的 
  2. 拆分到不同的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机制可以不用这么写了
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值