在JDBC(下)我会引入DBCP或者C3P0数据源来完善JdbcUtils。所以这里插播一下。
主要内容:
- 数据源的作用
- 为什么用代理模式/装饰者模式
- 自定义数据源:动态代理
- DBCP连接池部分源码解析
数据源的作用
之前提过,JDBC操作数据库,底层走的还是TCP协议。虽然我没专门学过计算机网络,但是也知道频繁开闭网络连接的时间开销是很大的,比如“三次握手”啥的。而数据源就是为了解决频繁创建销毁Connection所产生的时间开销问题。
在我看来,数据源最大的作用就是“复用Connection,减少时间开销”。
什么意思呢?每当我们调用DriverManager.getConnection(),底层会去调用driver.connect(),而connect()方法再往下就是很细节的网络连接。也就是说,DriverManager.getConnection()每次获取Connection,都会经历TCP的“三次握手”,以及数据库的各种校验,效率相当低。
而数据源的做法是:
- 项目启动时初始化固定数量的Connection,把创建连接的时间开销提前
- 用完Connection不是直接关闭,而是归还到连接池
- 当连接池现有的Connection不够时,才会去进行耗时的Connection创建
也就是说,连接池把创建10个Connection的时间开销提前到项目启动时,而不是等用户访问时才创建。如此一来,用户访问数据库的时间开销从原先的1s(创建)降低到了0.1s(从池中拿)。
而且程序用完Connection后调用connection.close()并不是销毁它,而是归还给连接池,达到了复用。
为什么用代理模式/装饰者模式
如果我们是直接调用数据源的close()方法,那完全可以把Connection归回给自身维护的连接池,确实用不到动态代理或者装饰者模式。
但是,难就难在我们返回给用户的往往就是一个Connection对象,即
Conncetion conn = dataSource.getConnection();
一顿骚操作之后...
conn.close();
而Connection本身close()的做法是:销毁连接。所以,我们必须“偷天换日”,悄悄地把connection.close()方法变成“将Connection归还连接池”,而不是实际关闭。
自定义数据源:动态代理
首先,我们来明确两个要点:
- 数据源>=连接池
- 数据源返回的Connection是代理对象