常见的数据源组件都实现了javax.sql.DataSource接口;
MyBatis不但要能集成第三方的数据源组件,自身也提供了数据源的实现;
一般情况下,数据源的初始化过程参数较多,比较复杂;
(采用工厂模式)
其实mybatis采用工厂模式,也是为了能适应更多的第三方数据源组件,如果有新的组件进来,只需要写类实现就行,工厂模式的优点很多,比如Spring的ioc,就是把对象的创建和对象的使用分离开来,解耦,new关键字耦合太多,对象的创建和管理由spring来做,而对象的使用是开发人员自己。
数据源模块的类图:
看一下DataSourceFactory接口:
这个接口的实现类有:
DataSourceFactory的实现类主要看 UnpooledDataSourceFactory这个类,也是一个工厂类。是一个不用连接池的数据链接工厂。
为什么说数据源很复杂,因为属性太多了,一般配置驱动。数据库url。用户名,密码等等实际上还可以配置很多
来看属性DataSource,这个也是一个接口
看DataSource接口的实现类PoolDataSource,使用连接池的数据源。
这个类还有很多初始化的构造函数,全是重载的。
所以才使用工厂来创建DataSource的对象/
再来回到这个图,根据代码也可以看到PooledDataSourceFactory继承了UnPooledDataSourceFactory
注意把逻辑分清楚:后缀带Factory的都是工厂类,不过有的是Unpool,有的是pool,而且pool的工厂类继承Unpool的工厂类,而Unpool的工厂类是用来生产Unpool的数据源,pool的工厂类是用来生产pool的数据源,看下图由虚线箭头的creat标志。
看UnpooledDataSource这个类,implements DataSource接口
这个问题来Debug一下:写一个Demo类,打断点
在UnpooledDataSource类的static的代码块中打上断点:
在DriverManager类的static的代码块中打上断点:
之后断点一直进入到上图LoadInitialDrivers();
然后进入这个load方法,打上断点
因为参数是Driver.class,所以ServiceLoader的常量service = Driver.class
那么来看构造方法
可以看到调用reload(),里面就是调用了new LazyIterator(service,loader),这是ServiceLoader类的内部类。
然后跳过,一直回到
在这个while循环里面跟进去, (上图是跟完以后的,下图是具体跟的步骤)
PREFIX 和 service 是什么 看下图:
然后 就去 这个目录下
parse解析
返回true ,然会while循环才进去
跟进next()
返回true,继续
就是在这块进行的。Class.forName,
然后CopyOnWritelist里面就保存了mysql驱动
这块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.loadblog.csdn.net
有兴趣的去看看,我的源码解读也很清楚
注意,这是mybatis的加载方式,jdbc的方式调用链还不太一样。可以写一个测试类用Class.forName()去debug看一下。
unpooledDatasource就是不用连接池的数据源,每次都是创建新的数据库连接。而pooledDatasource则不一样,会去从池子里找。
接下来看一下UnpooledDatasource是怎么产生数据库连接的
当 <dataSource>的type属性被配置成了”UNPOOLED”,MyBatis首先会实例化一个UnpooledDataSourceFactory工厂实例,然后通过.getDataSource()方法返回一个UnpooledDataSource实例对象引用,使用UnpooledDataSource的getConnection(),每调用一次就会产生一个新的Connection实例对象。
看一下:UnpooledDataSource的getConnection()
1.
初始化驱动:判断driver驱动是否已经加载到内存中,如果还没有加载,则会动态地加载driver类,并实例化一个Driver对象,使用DriverManager.registerDriver()方法将其注册到内存中,以供后续使用。
2.
创建Connection对象:使用DriverManager.getConnection()方法创建连接。
3.
配置Connection对象:设置是否自动提交autoCommit和隔离级别isolationLevel。
4. 返回Connection对象。看上图方法的第一行代码: initializeDriver(),画红框的不用多说了。
接着第二行代码:
Connection connection = DriverManager.getConnection(
url, properties);
每次都要生成新的connection, 开销很大,这就是没有连接池的数据源下来看有连接池的数据源 PooledDataSource : 一个简单,同步的、线程安全的数据库连接池,基于PooledConnection和PoolState
有几个对象需要知道:
1、PooledConnection:使用动态代理封装了真正的数据库连接对象Connection;
可以看到 PooledConnection 实现了 invocationhandler,动态代理。
真正的数据库连接对象是 Connection,作用是:连接数据库,获取查询数据,更新数据
那么 PooledConnection 是想对Connection进行增强。直接看invoke方法
2、PoolState:用于管理PooledConnection对象状态的组件,通过两个list分别 管理空闲状态的连接资源和活跃状态的连接资源
千万记住这两个ArrayList,以及ArrayList的remove方法的使用.
getConnection()获取连接的流程图
看一下获取有连接池的数据源 -> 获取 -> 数据库连接的代码:
PooledDataSource.getConnection()
获取连接就是在这个红框的 popConnection方法中做的。代码很长,逻辑不难,if - else判断比较多,和流程图一样,完全可以看懂,就不做分析了。
popConnection的代码:
注意这个数据结构,list是有序的集合,而remove(0)就是移除第一个并且返回。剩下的就不再分析了,代码很简单。关闭连接也有相应的代码,具体就不做分析了。