通过Mybatis源码去解析其运行原理

前记:上期讲了Mybatis的源码环境搭建,现在顺着这个往下学习。通过Debug方式去解析源码的执行流程来了解Mybatis的内部原理。

环境准备:

运行一下结果如下:

准备已经完成下面以Debug方式进入源码学习。

首先介绍一下Mybatis所起到的作用(这里可以去参考mybatis官网http://www.mybatis.org/mybatis-3/zh/index.html

官网的介绍:

MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生类型、接口和 Java 的 POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。

==========================开始进入源码=======================================

1、看测试案例

通过1和2获取到基本配置,然后通过SqlSessionFactoryBuilder这个类build方法去构建SqlSessionFactory工厂。

Debug进入build方法(从连接或数据源创建SqlSession)

这行代码比较重要,进入看看他做了什么

这里有个/configuration 有点熟悉不知道什么意思,但是中文翻译过来就是配置的意思,在看这个方法parseConfiguration翻译过来就是"解析配置",这里应该懂了,他应该是去解析之前准备的mybatis-config.xml的配置。 继续往下走看看

看到这里应该明白了他就是去解析配置的

看一下配置,明白过来了,这里也明白了为什么他的源码是 parseConfiguration(parser.evalNode("/configuration"));他为什么是"/configuration" 他是要去解析这个标签下的配置信息这里面的标签的含义,在mybatis官网有介绍的http://www.mybatis.org/mybatis-3/zh/configuration.html

这里我对这块源码加了注释(具体可以到官网学习)
private void parseConfiguration(XNode root) {
    try {
      //issue #117 read properties first
      /**解析配置文件中的各种属性*/
      propertiesElement(root.evalNode("properties"));
      /**解析mybatis的全局设置信息*/
      Properties settings = settingsAsProperties(root.evalNode("settings"));
      loadCustomVfs(settings);
      /**解析别名配置*/
      typeAliasesElement(root.evalNode("typeAliases"));
      /**解析插件配置*/
      pluginElement(root.evalNode("plugins"));
      /**解析对象工厂元素*/
      objectFactoryElement(root.evalNode("objectFactory"));
      objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
      reflectorFactoryElement(root.evalNode("reflectorFactory"));
      settingsElement(settings);
      // read it after objectFactory and objectWrapperFactory issue #631
      /**解析mybatis的环境配置*/
      environmentsElement(root.evalNode("environments"));
      databaseIdProviderElement(root.evalNode("databaseIdProvider"));
      /**解析类型处理器配置信息*/
      typeHandlerElement(root.evalNode("typeHandlers"));
      /**解析mapper配置信息*/
      mapperElement(root.evalNode("mappers"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
  }

现在拆解一下几个关键方法

插件加载:

通常有数据库特殊字段转化时(mysql:datatime->java:String)可以重写这拦截器方法

环境配置加载:

拿到我们的配置信息了

下面去看如何解析mapper文件的

看一下ProductMapper.xml的信息

返回在看如何解析mapper 我们进入到这个方法中->org.apache.ibatis.builder.xml.XMLConfigBuilder#mapperElement

进入org.apache.ibatis.builder.xml.XMLMapperBuilder#parse方法去详细了解如何解析sql语句,返回数据的

org.apache.ibatis.builder.xml.XMLMapperBuilder#configurationElement去如何操作

然后在看看如何解析sql语句的

获取到sql拼接的片段

 

(注意:这里解析是所有的sql并不是所有的查询语句,我注释写错了)

 

下面进入org.apache.ibatis.builder.xml.XMLMapperBuilder#bindMapperForNamespace 看看如何绑定映射器的、

 

在解析完配置后将信息(账号密码驱动等配置信息)保存在Configuration中去。返回后看一下

读取到配置信息了,还不止这些

总而言之,org.apache.ibatis.builder.xml.XMLConfigBuilder.parse解析了配置信息。

由上可知mybatis是如何获取数据源:

 >org.apache.ibatis.session.SqlSessionFactoryBuilder.build(java.io.InputStream) 
   >org.apache.ibatis.builder.xml.XMLConfigBuilder
  org.apache.ibatis.builder.xml.XMLConfigBuilder.parse
     >org.apache.ibatis.builder.xml.XMLConfigBuilder.environmentsElement
       >org.apache.ibatis.datasource.DataSourceFactory.getDataSource
         >org.apache.ibatis.session.Configuration.setEnvironment###### set数据源

也可以知道是如何构建SQL语句:

>org.apache.ibatis.session.SqlSessionFactoryBuilder.build(java.io.InputStream) 
    >org.apache.ibatis.builder.xml.XMLConfigBuilder
       >org.apache.ibatis.builder.xml.XMLConfigBuilder.mapperElement
         >org.apache.ibatis.builder.xml.XMLMapperBuilder
           >org.apache.ibatis.builder.xml.XMLMapperBuilder.parse
             >org.apache.ibatis.builder.xml.XMLMapperBuilder.buildStatementFromContext(java.util.List<org.apache.ibatis.parsing.XNode>)
               >org.apache.ibatis.builder.xml.XMLStatementBuilder.parseStatementNode
                >org.apache.ibatis.session.Configuration.addMappedStatement ######set 这个sql

 

 

=============================创建会话================================

.

下面就是创建一个会话了  SqlSession session = sessionFactory.openSession();

再看看Mybatis是如何创建会话的 f7进入

进入org.apache.ibatis.session.defaults.DefaultSqlSessionFactory#openSessionFromDataSource方法

获取Executor对象,进入org.apache.ibatis.session.Configuration#newExecutor(org.apache.ibatis.transaction.Transaction, org.apache.ibatis.session.ExecutorType)方法

从上面源码可知:1、默认SIMPLE类型的执行器,2、ExecutorType有三种(可以点开查看)3、cacheEnabled二级缓存是默认开启的。继续往下走他会去执行插件(拦截器,这里我们没配置,略过)

返回一个sqlSession对象

继续往下走:

如上图开始实例化ProductMapper(代理),如何生成?

knownMappers属性里面的值,实际上就是我们在mappers扫描与解析的时候放进去的。 下面进入org.apache.ibatis.binding.MapperProxyFactory#newInstance(org.apache.ibatis.session.SqlSession)如下:

看这个MapperProxy,点进去发现他是继承jdk的InvocationHandler接口,从以上代码可以看出,实际上使用的就是jdk的动态代理,给UserMapper接口生成一个代理对象。实际上就是MapperProxy的一个对象。如图:

所以整个代理对象生成过程可以用如下时序图表示:

=========================执行sql语句=======================

F7进入org.apache.ibatis.binding.MapperProxy#invoke

UserMapper实际上是代理对象MapperProxy,所以我们执行查询语句的时候实际上执行的是MapperProxy的invoke方法:

可以看到,先根据方法签名,从方法缓存中获取方法,如果为空,则生成一个MapperMethod放入缓存并返回。如下两段代码:

我们深入了解细节type,returnType status

type:进入org.apache.ibatis.binding.MapperMethod.SqlCommand#resolveMappedStatement 拿到方法与sql类型

returntype:org.apache.ibatis.binding.MapperMethod.MethodSignature#MethodSignature

status:org.apache.ibatis.reflection.ParamNameResolver#ParamNameResolver

继续执行(最终执行查询的是MapperMethod的execute方法):进入org.apache.ibatis.binding.MapperMethod#execute

根据上面拿到sql类型是查询SELECT进入查询方法,根据我们返回数据的类型(是多个many)

我们查询示例是list,然后进入org.apache.ibatis.binding.MapperMethod#executeForMany

进入org.apache.ibatis.reflection.ParamNameResolver#getNamedParams拿到参数(status的值)

参数处理完接下来就是调用执行过程,最终调用执行的是DefaultSqlSession中的selectList方法:org.apache.ibatis.session.defaults.DefaultSqlSession#selectList(java.lang.String, java.lang.Object, org.apache.ibatis.session.RowBounds)

获取到查询语句等一系列参数。

进入:org.apache.ibatis.executor.CachingExecutor#query(org.apache.ibatis.mapping.MappedStatement, java.lang.Object, org.apache.ibatis.session.RowBounds, org.apache.ibatis.session.ResultHandler)

进入:org.apache.ibatis.executor.CachingExecutor#query(org.apache.ibatis.mapping.MappedStatement, java.lang.Object, org.apache.ibatis.session.RowBounds, org.apache.ibatis.session.ResultHandler, org.apache.ibatis.cache.CacheKey, org.apache.ibatis.mapping.BoundSql)

他会去调用这个查询方法org.apache.ibatis.executor.BaseExecutor#query(org.apache.ibatis.mapping.MappedStatement, java.lang.Object, org.apache.ibatis.session.RowBounds, org.apache.ibatis.session.ResultHandler, org.apache.ibatis.cache.CacheKey, org.apache.ibatis.mapping.BoundSql)

先从缓存中去查,如果没有那就从数据库查询(org.apache.ibatis.executor.BaseExecutor#queryFromDatabase)

从数据库中查询,现在有个重要方法:org.apache.ibatis.executor.SimpleExecutor#doQuery如下:

进入:org.apache.ibatis.executor.statement.RoutingStatementHandler#RoutingStatementHandler

然后进入:org.apache.ibatis.executor.statement.BaseStatementHandler#BaseStatementHandler

返回来:进入org.apache.ibatis.executor.SimpleExecutor#prepareStatement

这是我们看下这个connection,为啥要看这个,因为我们看到一个方法prepareStatement(...)貌似是jdbc的方法,我们进入getConnetion方法(其实这个Connetion类就是在java.sql.Connection):

这回connction就是创建jdbc的链接。说明啥-->mybatis就是对jdbc的一种封装,变得更加便捷。

回到doQuery方法,进入:org.apache.ibatis.executor.statement.PreparedStatementHandler#query

然后进入:org.apache.ibatis.executor.resultset.DefaultResultSetHandler#getFirstResultSet

这里有个resultset

进入:org.apache.ibatis.executor.resultset.ResultSetWrapper#ResultSetWrapper

这里我们着重看一下这三个list:

发现这是将jdbc字段类型与转化为java的数据类型.

这里提出一句:ROM框架(对象关系映射(Object Relational Mapping))技术本质就是实现不同语言之间的不同类型数据之间的转换

继续往下走:

此时已经拿到结果集了,开始返回

此时我们的list就拿到结果了

说明从数据中已经查询到数据

那么查询数据的流程如下:

org.apache.ibatis.session.defaults.DefaultSqlSessionFactory.openSession()
 >org.apache.ibatis.session.defaults.DefaultSqlSessionFactory.openSessionFromDataSource
  >org.apache.ibatis.session.Configuration.newExecutor(org.apache.ibatis.transaction.Transaction, org.apache.ibatis.session.ExecutorType)
    >org.apache.ibatis.session.defaults.DefaultSqlSession.DefaultSqlSession(org.apache.ibatis.session.Configuration, org.apache.ibatis.executor.Executor, boolean)
      >org.apache.ibatis.session.defaults.DefaultSqlSession.selectOne(java.lang.String, java.lang.Object)
        >org.apache.ibatis.executor.SimpleExecutor.doQuery
           >org.apache.ibatis.executor.statement.PreparedStatementHandler.query
             >org.apache.ibatis.executor.resultset.ResultSetWrapper.ResultSetWrapper

 

至此,mybatis源码执行流程就分析结束了。mybatis的源码没有注释,但是可读性很强。
 

ps:源码学院-专门为培养bat职员而生,老师讲的很好,知识也很全面,当然还是需要自己努力。详情加群:687721065

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值