浅谈 Mybatis源码的执行过程

Mybatis是一个半自动化的ORM框架,相对于Hibernate来说,是轻量级的,而且对于自定义sql有着非常友好的用户体验,使用也方便。现在我们来粗略探索下Mybatis是怎么帮助我们对数据库进行操作并映射的。

我们先来说说 Mybatis 的执行流程,先看使用的代码:

(图1)

我们就以这个来说明Mybatis的执行过程。Mybatis 的使用首先需要有4个关键对象,分别是SqlSessionFactoryBuilder、SqlSessionFactory、SqlSession和对应需要操作的Mapper。简单的可以这么说, SqlSessionFactoryBuilder 可以生产出 SqlSessionFactory,SqlSessionFactory 可以获得SqlSession,而SqlSession可以得到对应的Mapper,具体的操作和生命周期可以看《Mybatis 之创建时的4个核心对象的生命周期》。接下来我们来进入源码看流程。

首先是看 SqlSessionFactory 如何被创建。进入 SqlSessionFactoryBuilder.build(InputStream inputStream) 方法的源码看到  它里边又调用了子集的一个builder方法(75行)(图2)

其中首先根据传入的配置文件(mybatis-config.xml)去创建一个XMLConfigBuilder,用于解析配置文件,解析的过程为78行的parser.parse() 方法

(图3)

如图所示,将配置文件里边的各个configuration下的标签一一解析。这里我们重点是看解析 mappers(120行)

(图4)

进入到了XMLConfigBuilder.mapperElement方法,源码会判断配置的mapper文件的类型是package类型还是其他类型,我们这里直接走 381行 的解析文件类型

(图5)

这里有两个关键的地方1和2,分别为解析和绑定。我们先进入1

 (图6)

这里其实就是在配置我们自定义的xxxMapper.xml文件,里边的参数都是文件里边的配置,包括resultMap、sql、cache等等。我们直接去关键的部分121行

(图7)

这里进入127行的 buildStatementFromContext 方法,在131行又进入了134行的方法中,继续进看138行,进入里边的方法

(图8)

这个方法就是在解析mapper配置文件里边 增删改查标签 中的配置,直接看到方法最后的一句,这个是重点

(图9)

进入这个方法

(图10)

这里将会生成一个 MappedStatement ,里边就含有配置configuration/sql、返回对象、形参等,看该方法倒数第二行并进入

(图11)

(图12)

调用了Configuration里边的方法,存放了刚刚生成的 MappedStatement 到 mappedStatements,而 mappedStatements 又是 刚刚Configuration中的一个属性map。

到此已经把包装了sql、形参等信息的 MappedStatement 放到了 Configuration 中,这结论先记着,很重要

回到图5的2中

(图13)

获得了mapper的namespace,并调用了configuration的addMapper方法,传入了namespace对应的mapper的class类型,点进去

(图14)

图(15)

这里的操作就是将namespace对应的class类型(即mapper接口的class)和一个MapperProxyFactory绑定一起,放到knownMappers中,而这又是一个hashmap,而knownMappers是在MapperRegistry中,MapperRegistry在Configuration中,即最后还是放到Configuration中,至此,解析配置文件结束

回到图2的78行 最后是 return(parser.parse()) ,即返回一个解析后的Confuguration,点进去调用了下边的方法,返回一个DefaultSqlSessionFactory

(图15)

到这里 SqlSessionFactory 已经被创建出来了,并且持有已经解析好了的Configuration对象

然后就是sqlSessionFactory.openSession()方法获得SqlSession。我们进入DefaultSqlSessinFactory的openSession方法,最后是调用了自己的一个重载方法

(图16)

其中1创建了事务,2创建了执行器,3创建并返回了持有执行器对象 executor 的 DefaultSqlSession。创建执行器的过程如下:

(图17)

会根据类型去创建对应的执行器(BATCH/REUSE/SIMPLE),假如开启了缓存,会再包一层, 创建成CachingExecutor。

到这里SqlSession也创建出来了

然后就是获取Mapper对象 sqlSession.getMapper()。进入源码

(图18)

如图,调用了configuration.getMapper方法,而这个configuration就是前边解析配置文件后存放所有配置信息的configuration。继续跳进源码,一直跳到关键方法如下图

(图18)

有两个关键地方。1是从knownMappers中根据classType获得对应的MapperProxyFactory,而这个knownMappers就是我们在前边解析的时候数据存进去的(看图15)。然后2传入sqlSession调用了newInstance方法。

(图19)

接着来到了方法1,先创建一个MapperProxy,然后又调用了方法2,用jdk动态代理方式生成了MapperProxy的一个代理对象并返回,这个就是最后得到的Mapper对象(代理对象),这也是为什么Mapper接口不需要实现的原因,因为最后被实例出来的只是一个代理对象。

最后是调用Mapper的具体数据库操作的方法(如 mapper.getById())。由以上知道mapper只是拿到一个代理对象,故执行的时候将会进入 MapperProxy 的 invoke方法

(图20)

先判断调用的方法是否是Object本身(如toString/equals等),是的话直接反射调用,不是的话走85行。先走cachedInvoker(method)方法,去configuration中获得对象的sql信息,然后再调用invoke方法,点进去

(图21)

 (图22)

这时就会根据上一步cachedInvoker(method)后得到的sql信息对应的走特定流程,例如走87行的selectOne(),就进入了DefaultSqlSession中

(图23)

继续点,最后来到了selectList方法

(图24)

这里可以看到,其实底层用到了Executor执行器去执行数据库操作,接下来到了 BaseExecutor中

(图25)

(图26)

(图27)

从这里开始,就开始进入具体执行器中的代码,例如SimpleExecutor

(图28)

首先将前边获得的sql等信息封窗成handler,然后封装参数,并继续调用query方法

(图29)

从这里开始,就是jdbc的执行操作了,mybatis层已经走到底了。然后就是执行sql并返回结果。以上就是mybatis的执行流程,整合Spring后,sqlSession就不再用DefaultSqlSession(因为DefaultSqlSession不是线程安全的),而是用了SqlSessionTemplate。后边会抽空再出Spring整合mybatis后mybatis的执行流程,会和以上有些许不同,如 Mapper 的注入和session的开启和关闭,都无需手动控制。最后再来几张流程图

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值