还是首先来个流程图
下面给个用例看看,接口如何调用的
这里与mybatis的接口调用有三个,一个是opensession 第二个是getMapper获取动态代理类对象.注意studentmapper.class只是一个interface.里面的接口调用全靠动态代理实现.第三个就是method调用了.现在就开始由第一个方法调用开始分析
1.openSession
这里首先获取我们配置文件里面的environment数据
其次是根据environment的type属性获取对应的transactionfactory对象.这里type一般是JDBC,所以应该是JdbcTransactionFactory的对象
再次是调用newTransaction生成具体的对象.这里是JdbcTransaction的对象.上面流程图没画出来空间不够了.
再次,是根据这给出的参数生成executor.这是个很重要的对象,sql的操作在这里进行中转
最后就是生成一个默认的DefaultSqlSession对象.返回给调用者
2.getMapper
当上面获取Sqlsession句柄后,就开始获取interface的动态代理对象了.代码截图
经过上面三个getMapper调用.最终给出的是一个mapperproxyfactory.newInstance生成的对象.那么这个类是如何定义,而接口又是如何实现的呢看代码:
看到InvocationHandler接口没.这个是标准的动态代理了.所以我们getMapper就是获取了一个动态代理类.上层的方向调用将会被MapperProxy类的invoke拦截.在这里我们就可以实现sql相关的操作了
3.method调用.这个是重点.涉及到了很多的类,这里只大概过一遍.知道是逻辑是怎么实现的,以后有机会再重点看细节.先看代理类的invoke拦截方法:
这里面有两个判断:
1.判断是不是Object对象级别的方法调用.如果是直接调用就是了.如何hashcode调用
2.判断这个方法是不是 public方法并且该方法不是abstract有具体的实现
3.上面两个都不成立.就论到sql来操作了.我们来看execute是怎么实现的.
3.execute
这里一眼就看出来是执行sql相关操作了吧.这里首先获取sql的操作类型,这个数据来源是来自配置文件,这里我在把代码再截一下,
就是根据节点的头来得到commandtype.
在这里我们选择select这个最常见的操作来跟踪是如何一步一步执行sql的.
首先会根据method的返回值的不同来调用不同的处理方法.这里从选择executeForMany来跟踪(如果判断返回值类型,这个好像有点复杂.没太看明白)截图
这里很明显就selectList这个函数是核心.再继续跟踪selectList这个函数.
4.selectList方法是sqlsession的方法.这里的session是DefaultSqlSession
这里首先得到sql操作的配置数据.然后再调用执行者的query接口.而这个执行者对象,我们一开始使用的是simpleExecutor.
5.query
这个query是调用simpleExecutor的父类BaseExecutor的接口.看实现
这看首先是读取我们配置的sql命令了. BoundSql boundSql = ms.getBoundSql(parameter);就是这句话了,找了这么久终于找到加载的地方了.
然后根据传递进来的参数生成key.然后再根据key到缓存中去查找有没有对应的数据.
如果缓存里面有则调用handleLocallyCachedOutputParameters这个函数来处理可能存在的out参数(我们知道一般参数分in 和 out两种,或者同一个参数同时有两种属性)然后返回.
如果缓存中没有则从数据库查找.找到的值在缓存中存一份.
需要注意的是这里的缓存是所谓的第一级缓存,一定存在的跟配置无关.
6.queryFromDatabase
我们如何从数据库读取数据.截图如下
这里的代码意思就很明显了.先在缓冲在抢个位置.
然后执行doQuery
然后再清掉这个key让出位置.我估计这是为多线程服务的.当多个线程都操作到这个里了.发现这个key的特殊标记,会过直接返回过一会再来查询
再然后就是将真正的数据放到缓存中去.同时如果参数存在out参数.则进行out处理.
7.doQuery
这个是simpleExecutor里面实现的,看代码截图
到这里就开始找数据库了.这里会比较绕,绕来绕去了.
首先调用configuration创建一个handler,代码截图
这里生成了一个RoutingStatementHandler对象.我们自己定义的插件,都是在这里以拦截的方式上车的.然后看下这个类的构造函数.
这里一看就知道,这个类是个adapter类,根据类型选择合适的handle.这里我们用simple
handler生成完毕之后.prepareStatement函数调用生成Statement对象.
这两个对象都生成完调用handler.query来查询.
这里很清楚了,就先获取sql语句.然后调用execute了.
所以传进来的statement是关键.看其生成的函数prepareStatement
这里获取sql标准的类Connection. 其transaction是JdbcTransaction对象.看JdbcTransaction对象里面的getConnection接口实现
接下去就是一些标准的数据库操作了.只不过这里又进了一些类的封装.跟踪下去涉及到多个其它类,这里流程图没空间画出来.这里也不继续说了.
到这里就差不多了说下后面的思路吧.通过我们配置的environments来与数据库建立连接取得Connection对象.然后再用Connection对象的createStatement方法生成Statement对象.再然后就是调用Statement.execute(String sql) 来执行sql操作了.
到此就结束了,欢迎大家指出问题