myBatis执行流程及源码分析

Posted 八阿哥克星

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了myBatis执行流程及源码分析相关的知识,希望对你有一定的参考价值。

一、简介

        MyBatis是一个实现了JPA规范的用来连接数据库并对其进行增删改查操作的开源框架 ,其实,它的底层就是一个JDBC封装的组件,因此MyBatis在使用上免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。

那接下来,就来看一下MyBatis到底是如何执行的。

 

二、执行流程

        

 

1、获取sqlsessionFactory

     解析文件的信息保存到Configuration中,返回Configuraation的DefaultSqlSession对象;【mappedStatement】代表一个增删改查的详细信息

2、获取sqlSession。返回sqlSession的实现类DefaultSqlSession,里面维护了Excutor(执行器)和Configuration(配置对象)。

3、获取接口代理对象MapperProxy,返回的是动态代理对象,里面维护了上面的sqlsession,也是真正执行增删改查的对象。

4、执行增删改查

 

三、源码分析

一、获取sqlsessionFactory。SqlSessionFactoryBuilder最终会走到下图的方法。

 

下一步,SqlSessionFactoryBuilder走到build()方法,并带着configuration对象执行DefaultSqlSessionFactory的有参构造创建工程对象。

 

那有朋友就会说,configuration对象包含哪些属性,他有事怎样赋值的呢,别着急,下面就一起来看一下。

我们知道,关键在于上面提到的parse()方法,那就看看他里面都有什么?

 

可以看到,实际上parseConfiguration()才是parse()方法的核心,我们进到里面看

 

这里,可以看到,是将文档对象中的节点也就是xml配置文件中的标签的数据赋到了configuration的对应属性上

我们着重看一下mapperElement()方法:

 

可以看到,mapper映射文件的配置有多种形式,扫描包,解析url路径等等,形式虽然不同,但是做的事情都是一样的,那就以package为例, 进入到他的addMappers()方法中一探究竟:

addMappers()扫描到的mapper接口一一遍历,作为参数执行到了下面的方法:

 

可以看到,这里是为每个mapper接口创建了对应的代理对象工厂MapperProxyFactory,是为了后面创建mapper代理对象用,并将工厂对象存放到configuration维护的一个集合中。下面是将每个mapper接口和其对应的映射文件以方法为粒度封装到了mappedstatements对象中,其中包含了操作数据库需要用的如方法名称、sql语句等信息

进行到这里,可以说mybatis执行流程的第一步就完成了。

二、获取sqlsession对象

        在拿到sqlsessionFactory对象后,紧接着的一个操作就是获取真正操作数据库的sqlsession,那么,到底是如何获取以及获取的同时还做了什么事情,我们往下看

首先,openSession()调用了openSessionFromSource()方法,后面传的参数其实是维护在configuration中默认执行器的类型,这个参数的用途稍后就可以看到;

进入到openSessionFromSource()方法中,可以看到,这个才是openSession的核心所在,mybatis在这里初始化了一些sqlSession所需成员变量,并且创建了DefaultSqlSession对象

private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
    Transaction tx = null;
    try {
//      获取环境信息
      final Environment environment = configuration.getEnvironment();
//      获取environment节点下的事务配置信息 默认情况下是ManagedTransactionFactory,
//      如果配置了transactionManager节点并且type为JDBC,则返回JdbcTransactionFactory事务工厂
      final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
      //通过事务工厂来产生一个事务,
      tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
      //生成一个执行器(事务包含在执行器里)
      final Executor executor = configuration.newExecutor(tx, execType);
      //然后产生一个DefaultSqlSession
      return new DefaultSqlSession(configuration, executor, autoCommit);
    } catch (Exception e) {
      //如果打开事务出错,则关闭它
      closeTransaction(tx); // may have fetched a connection so lets call close()
      throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);
    } finally {
      //最后清空错误上下文
      ErrorContext.instance().reset();
    }

这样,sqlSession也就"open"出来了。

三、获取mapper接口代理对象

        很多朋友初用mybtis是一定有这样的疑问,我明明只是创建了mapper接口,并没有实现,为什么却能拿到可执行的对象呢,这就是我们接下来要探讨的代理对象的创建过程:

        上面第一步时,我们分析过configuration对象里维护了mapper接口和mapper映射文件的信息属性和生成mapper接口代理对象的工厂MapperProxyFactory,到这一步时,就是拿到对应接口的工厂,利用JDK动态代理技术生成我们最后真正使用的MapperProxy代理对象。

        DefaultSqlSession.getMapper()方法调用的其实是维护在DefaultSqlSession对象中的configuration对象的同名方法,第一步解析配置文件的时候,我们提到MapperProxyFactory最终是存放到configuration下的MapperRegistry下的Map<Class<?>, MapperProxyFactory<?>> knownMappers这么一个集合中,所以呢,这时当然也会从它里取出MapperProxyFactory

取出之后,当然就是用工厂来创建代理对象了,也mapperProxyFactory.newInstance(sqlSession)

它里面是这样的,我们可以看到,先是调用有参构造创建MapperProxy,其实MapperProxy是实现了InvocationHandler接口的,熟悉JDK动态代理的朋友,看到这个类就非常明白接下来的操作了,没错,就是调用Proxy.newProxyInstance方法最终生成指定的mapper接口代理对象。

这样呢,第三步生成代理对象也就完成了,最后一步因为过程稍复杂,有时间再和朋友们一起研究;

 

 

 

 

以上是关于myBatis执行流程及源码分析的主要内容,如果未能解决你的问题,请参考以下文章

mybatis执行流程源码分析

深入浅出Mybatis系列---SQL执行流程分析(源码篇)(转)

MyBatis源码分析-SQL语句执行的完整流程

MyBatis源码分析-SQL语句执行的完整流程

MyBatis源码分析select源码分析及小结

mybatis源码分析—运行流程及原理