一个数据表连接两个数据源_Mybatis源码分析(二): 数据源模块

常见的数据源组件都实现了javax.sql.DataSource接口;

MyBatis不但要能集成第三方的数据源组件,自身也提供了数据源的实现;

一般情况下,数据源的初始化过程参数较多,比较复杂;

(采用工厂模式)

3fd6ab12790f39d28955914023b30ef0.png

其实mybatis采用工厂模式,也是为了能适应更多的第三方数据源组件,如果有新的组件进来,只需要写类实现就行,工厂模式的优点很多,比如Spring的ioc,就是把对象的创建和对象的使用分离开来,解耦,new关键字耦合太多,对象的创建和管理由spring来做,而对象的使用是开发人员自己。


数据源模块的类图:

4ff1924905b62b70a5c84f0e85802b59.png

看一下DataSourceFactory接口:

d451f5ff006557131e1be6203b793a44.png

这个接口的实现类有:

e8a7e410537b5056867a6391b6a95cce.png

DataSourceFactory的实现类主要看 UnpooledDataSourceFactory这个类,也是一个工厂类。是一个不用连接池的数据链接工厂。

为什么说数据源很复杂,因为属性太多了,一般配置驱动。数据库url。用户名,密码等等实际上还可以配置很多

66e4e223daa05d360b26498d1c484376.png

59eb676c53b208e73fdcd159a60cfcc3.png

来看属性DataSource,这个也是一个接口

1617dc1a68a10febd272218f202ac9dc.png

8f108cf9e63982cbb8fb5a94812d4467.png

看DataSource接口的实现类PoolDataSource,使用连接池的数据源。

a74b8f549efa5ef732cf4bad00035b15.png

这个类还有很多初始化的构造函数,全是重载的。

0ed88d4e9a25fbd49c9c8ec06ba76a81.png

所以才使用工厂来创建DataSource的对象/


再来回到这个图,根据代码也可以看到PooledDataSourceFactory继承了UnPooledDataSourceFactory

2c48bfbe71f929617e5ae7b8a43f93fc.png

8dc8130ebd63df675ed8f792c7ce22f3.png

注意把逻辑分清楚:后缀带Factory的都是工厂类,不过有的是Unpool,有的是pool,而且pool的工厂类继承Unpool的工厂类,而Unpool的工厂类是用来生产Unpool的数据源,pool的工厂类是用来生产pool的数据源,看下图由虚线箭头的creat标志。

df9be50bba3737eec5b89bfe43be41ec.png

看UnpooledDataSource这个类,implements DataSource接口

e20b30f10cdd37ead112a88864b20a57.png

这个问题来Debug一下:写一个Demo类,打断点

ecbed4ec98fbea20d55f90d24a90502f.png

在UnpooledDataSource类的static的代码块中打上断点:

cb3a22151ee630e1aba20ae98b0deb0d.png

在DriverManager类的static的代码块中打上断点:

37024ab9ac74b34546243c684fa2eeea.png

之后断点一直进入到上图LoadInitialDrivers();

d935fa0eeff7b082ea678a9eab575336.png

然后进入这个load方法,打上断点

6eae452e9571a742472ee5fec4bb83a5.png

因为参数是Driver.class,所以ServiceLoader的常量service = Driver.class

c4213e5c0ce2cd3839e093f346c8dfda.png

505a8cfcfd3e8b065d01006829edaae1.png

那么来看构造方法

87a8be8c5c49fd12d7188d155ffc5a41.png

可以看到调用reload(),里面就是调用了new LazyIterator(service,loader),这是ServiceLoader类的内部类。

80fb5d57b2591a3743ca4b8e71f2df30.png

然后跳过,一直回到

dd74cce1761d40edf15c89c10cd7f941.png

在这个while循环里面跟进去, (上图是跟完以后的,下图是具体跟的步骤)

650adf4fac6a397907b6ae095b5a3955.png

PREFIX 和 service 是什么 看下图:

270167a830e1b2ad757569a2ae6a7f2d.png

然后 就去 这个目录下

1ac9ab5997993c5d757e1967928e7955.png

c2ef7d6d46d76dcf223976714edc1270.png

parse解析

bce78b6c3cc13edc32e836e1a7e5b74e.png

772fba441c0204637c08ac9d080dba01.png

返回true ,然会while循环才进去

93d15fb140e1326491aa238571caaf59.png

d35e0cda149a8b58d5f85b976211bb87.png

跟进next()

cbc77a1bba836682265e89e8ba7f72ef.png

40198c2c9413054d5031f93123b7a836.png

3d07cbcb41997eb6fb56db6d7aed1688.png

返回true,继续

372ef922f424529d5ad5dd47bcd15e1e.png

就是在这块进行的。Class.forName,

然后CopyOnWritelist里面就保存了mysql驱动

61c0027eec17d2f54dd538687292a096.png

这块ServiceLoader.load()属于Java SPI的知识,其本质就是服务的提供者,提供了服务接口的一种实现之后,在jar包的META-INF/services/目录里同时创建一个以服务接口命名的文件。该文件里就是实现该服务接口的具体实现类。而当外部程序装配这个模块的时候,就能通过该jar包META-INF/services/里的配置文件找到具体的实现类名,并装载实例化,完成模块的注入。 java.util.ServiceLoader工具类方法会使用ClassLoad类的getResources方法获取指定目录下的文件,读取文件内容并解析获取所有接口实现类的全限路径,根据全限路径使用class.forName装载class文件到JVM,然后通过c.newInstance()实例化类转化接口类型并放入providers缓存提供者集合中,以供后面使用。上面这段话是摘自

JavaSPI,ServiceLoader.load方法详解_hupoling的专栏-CSDN博客_serviceloader.load​blog.csdn.net
e12d6400fc9681f4a4925673f64fc306.png

有兴趣的去看看,我的源码解读也很清楚

注意,这是mybatis的加载方式,jdbc的方式调用链还不太一样。可以写一个测试类用Class.forName()去debug看一下。

unpooledDatasource就是不用连接池的数据源,每次都是创建新的数据库连接。而pooledDatasource则不一样,会去从池子里找。

接下来看一下UnpooledDatasource是怎么产生数据库连接的

当 <dataSource>的type属性被配置成了”UNPOOLED”,MyBatis首先会实例化一个UnpooledDataSourceFactory工厂实例,然后通过.getDataSource()方法返回一个UnpooledDataSource实例对象引用,使用UnpooledDataSource的getConnection(),每调用一次就会产生一个新的Connection实例对象。

看一下:UnpooledDataSource的getConnection()

1c3020f2de2028a3b8cbfe234036b1e8.png

d0846a3df28453fd7ab4d38f0bdc9e1d.png

715e1e4444f58b12272c5c43ad81a0df.png

dbcd7cf277b729dbf3aedeb3dd93a94b.png

1. 初始化驱动: 判断driver驱动是否已经加载到内存中,如果还没有加载,则会动态地加载driver类,并实例化一个Driver对象,使用DriverManager.registerDriver()方法将其注册到内存中,以供后续使用。
2. 创建Connection对象: 使用DriverManager.getConnection()方法创建连接。
3. 配置Connection对象: 设置是否自动提交autoCommit和隔离级别isolationLevel。4. 返回Connection对象。

看上图方法的第一行代码: initializeDriver(),画红框的不用多说了。

e3f5d04b6edc5554c4ccb24ff190d87c.png

接着第二行代码:

Connection connection = DriverManager.getConnection(url, properties);

dbcd7cf277b729dbf3aedeb3dd93a94b.png

每次都要生成新的connection, 开销很大,这就是没有连接池的数据源


下来看有连接池的数据源 PooledDataSource一个简单,同步的、线程安全的数据库连接池,基于PooledConnection和PoolState

有几个对象需要知道:

1、PooledConnection:使用动态代理封装了真正的数据库连接对象Connection;

可以看到 PooledConnection 实现了 invocationhandler,动态代理。

3e9235d2d54a9b9f609b580f767d3be6.png

真正的数据库连接对象是 Connection,作用是:连接数据库,获取查询数据,更新数据

那么 PooledConnection 是想对Connection进行增强。直接看invoke方法

c1120c88eeb7cae8eacb94b0212ab35a.png

2、PoolState:用于管理PooledConnection对象状态的组件,通过两个list分别 管理空闲状态的连接资源和活跃状态的连接资源

e814989e1edfd57a6145be2d1e740c2b.png

千万记住这两个ArrayList,以及ArrayList的remove方法的使用.

getConnection()获取连接的流程图

4d970717f47b64ebf42c2cfee631ab78.png

看一下获取有连接池的数据源 -> 获取 -> 数据库连接的代码:

PooledDataSource.getConnection()

d7c13507aa3060e927ab820a32dc7fc8.png

获取连接就是在这个红框的 popConnection方法中做的。代码很长,逻辑不难,if - else判断比较多,和流程图一样,完全可以看懂,就不做分析了。

popConnection的代码:

0fd7f50c5b3f410e0bdfc7079cccf3e4.png

注意这个数据结构,list是有序的集合,而remove(0)就是移除第一个并且返回。剩下的就不再分析了,代码很简单。关闭连接也有相应的代码,具体就不做分析了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Go语言(也称为Golang)是由Google开发的一种静态强类型、编译型的编程语言。它旨在成为一门简单、高效、安全和并发的编程语言,特别适用于构建高性能的服务器和分布式系统。以下是Go语言的一些主要特点和优势: 简洁性:Go语言的语法简单直观,易于学习和使用。它避免了复杂的语法特性,如继承、重载等,转而采用组合和接口来实现代码的复用和扩展。 高性能:Go语言具有出色的性能,可以媲美C和C++。它使用静态类型系统和编译型语言的优势,能够生成高效的机器码。 并发性:Go语言内置了对并发的支持,通过轻量级的goroutine和channel机制,可以轻松实现并发编程。这使得Go语言在构建高性能的服务器和分布式系统时具有天然的优势。 安全性:Go语言具有强大的类型系统和内存管理机制,能够减少运行时错误和内存泄漏等问题。它还支持编译时检查,可以在编译阶段就发现潜在的问题。 标准库:Go语言的标准库非常丰富,包含了大量的实用功能和工具,如网络编程、文件操作、加密解密等。这使得开发者可以更加专注于业务逻辑的实现,而无需花费太多时间在底层功能的实现上。 跨平台:Go语言支持多种操作系统和平台,包括Windows、Linux、macOS等。它使用统一的构建系统(如Go Modules),可以轻松地跨平台编译和运行代码。 开源和社区支持:Go语言是开源的,具有庞大的社区支持和丰富的资源。开发者可以通过社区获取帮助、分享经验和学习资料。 总之,Go语言是一种简单、高效、安全、并发的编程语言,特别适用于构建高性能的服务器和分布式系统。如果你正在寻找一种易于学习和使用的编程语言,并且需要处理大量的并发请求和数据,那么Go语言可能是一个不错的选择。
Go语言(也称为Golang)是由Google开发的一种静态强类型、编译型的编程语言。它旨在成为一门简单、高效、安全和并发的编程语言,特别适用于构建高性能的服务器和分布式系统。以下是Go语言的一些主要特点和优势: 简洁性:Go语言的语法简单直观,易于学习和使用。它避免了复杂的语法特性,如继承、重载等,转而采用组合和接口来实现代码的复用和扩展。 高性能:Go语言具有出色的性能,可以媲美C和C++。它使用静态类型系统和编译型语言的优势,能够生成高效的机器码。 并发性:Go语言内置了对并发的支持,通过轻量级的goroutine和channel机制,可以轻松实现并发编程。这使得Go语言在构建高性能的服务器和分布式系统时具有天然的优势。 安全性:Go语言具有强大的类型系统和内存管理机制,能够减少运行时错误和内存泄漏等问题。它还支持编译时检查,可以在编译阶段就发现潜在的问题。 标准库:Go语言的标准库非常丰富,包含了大量的实用功能和工具,如网络编程、文件操作、加密解密等。这使得开发者可以更加专注于业务逻辑的实现,而无需花费太多时间在底层功能的实现上。 跨平台:Go语言支持多种操作系统和平台,包括Windows、Linux、macOS等。它使用统一的构建系统(如Go Modules),可以轻松地跨平台编译和运行代码。 开源和社区支持:Go语言是开源的,具有庞大的社区支持和丰富的资源。开发者可以通过社区获取帮助、分享经验和学习资料。 总之,Go语言是一种简单、高效、安全、并发的编程语言,特别适用于构建高性能的服务器和分布式系统。如果你正在寻找一种易于学习和使用的编程语言,并且需要处理大量的并发请求和数据,那么Go语言可能是一个不错的选择。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值