Mybatis 源码跟踪

本篇就是记流水账的,一般孩儿可忍不了

在前面一篇博客中,我从官方文档上抄了这么一段内容,它是一段完整的mybatis执行步骤的代码。首先创建sqlSessionFactory ,在利用它获取一个SqlSession对象,这个SqlSession对象通过getMapper方法获取一个MapperProxy的代理对象,并利用代理对象执行增删改查的逻辑。

String resource = "mapper/mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
try{
  BlogMapper mapper = sqlSession.getMapper(XXXX.class)
  mapper.....
}finally{
  sqlSession.close();
}

SqlSessionFactory

SqlSessionFactoryBuilder.build(resource) 的方法

创建SqlSessionFactory阶段最重要的就是解析所有内容。第一个Parser类解析全局配置文件,存入Configuration对象中。在解析所有的mapper.xml内容,将每一个表示SQL的语句包装成一个MappedStatement对象,这个对象包含了SQL语句的所有信息。并将所有的MappedStatement存放到Configuration中,用一个全类名Mapper.id的形式(如com.zengg.test.dao.EmployeeMapper.getEmpById)的形式一一对应,并生成一个DefaultSqlSessionFactory对象,构造器存入Configuration。
此外呢,Configuration还有一个属性MapperRegistry,这个里面一个namespace的接口类对应一个MapperProxyFactory,相当于以后的MapperProxy是用这个工厂生产的。

1、首先会创建一个XMLConfigBuilder的类对象,顾名思义,它的作用是解析全局配置文件的类,并执行解析方法。


2、XMLConfigBuilder.parse()方法中的parseConfiguration(XNode root)方法同根节点开始,将所有可能涉及到的配置标签如properties、typeAliases等信息全部封装到Configuration对象中,包括Setting里面的值,没有配置的就使用默认值填充

3、然后返回这个Configuration 对象并创建一个new DefaultSqlSessionFactory(config)
到这儿SqlSessionFactory就创建好了,它只是获取了全局配置文件的内容并新建默认对象DefaultSqlSessionFactory。

SqlSession

SqlSessionFactory.openSession()

这个阶段主要是生成一个Executor的执行器,根据configuration中的不同的executorType,生成不一样的执行器(REUSE/SIMPLE(默认)/BATCH)。如果有缓存,再包装一次成CacheExecutor(实际还是用之前生成的执行器操作,包装了一层缓存而已),然后将Executor和Configuration作为构造器的一部分,生成一个DefaultSqlSession 对象

1、在DefaultSqlSessionFactory类中执行openSession()方法,这个方法首先从全局配置对象Configuration中获取Environment标签的信息中的事务管理器和ExecutorType,并创建一个重要对象Executor(执行器),不同的ExecutorType执行器类型,会创建不同的执行器。

2、如果我们开启了二级缓存,mybatis会将已经创建的执行器包装成一个新的扩展执行器对象CachingExecutor。这个缓存执行器里面的通用的逻辑依然是用的之前创建的执行器,不过是在外加载了一层缓存相关处理的方法。

3、将Configuration、Executor 封装创建一个DefaultSqlSession对象并返回。

MapperProxy

sqlSession.getMapper(Class<T> type)

重要的就是使用MapperProxyFactory创建了MapperProxy对象,对象里包含了DefaultSqlSession(Executor和configuration)的内容
1、getMapper()方法实际上是调用了Configuration的getMapper方法(传入了sqlSession对象作为参数),再调用MapperRegistry类对象去创建。
2、在MapperReistry类中,显示根据参数MapperDao的全类名为key,获取到对应的MapperProxyFactory,然后在利用这个MapperProxyFactory新建实例MapperProxy,这个MapperProxy就是我们用的代理对象了。它底层是利用了JDK下的java.lang.reflect包去实现的。

执行查询

匹配MappedStatement,拿到语句的内容,为查询做准备。执行查询的逻辑由SqlSession执行(包含了Executor和Configuration对象)
1、MapperProxy实现了接口InvocationHandler,它执行方法的时候新进入它的invoke()方法。这个方法中传入三个参数,分别是proxy对象、方法接口以及参数。
     然后创建一个MapperMethod对象并调用execute方法。
2、根据当前方法的类型(增删改查)分别执行不同的逻辑


3、当前跟踪查询方法,不同的返回参数有不同的MappedStatement执行逻辑(分页、查询条件),比如说返回值为map,那么执行结果就是 (SqlSession执行查询)
result = sqlSession.selectList(this.command.getName(), param);
如果返回值就是一个数,也会返回一个集合,只不过会再去集合的第一个值,因此它们执行查询的逻辑基本相似。

以最下面的selectOne为例(返回值为单个对象),继续跟踪。继续执行DefaultSqlSession的查询类,返回值为List并只取第一个作为结果返回。"参数中statement实际上就是sql的唯一标识全类名加配置语句的唯一id")

4、利用封装在DefaultSqlSession中的执行器Executor 进行查询逻辑。Executor有两个实现类,一个是基础实现类BaseExecutor,另外一个是CachingExecutor缓存执行器。这里我进入缓存执行器查看逻辑。

先回从configuration中对比传入的statement参数,拿到唯一的MappedStatement对象(这个对象封装了该执行语句的所有配置信息)



Executor

1、接续上面的逻辑,SqlSession调用Executor去执行的query的方法,这个方法第一步就会生成一个BoundSql的对象,这个对象的作用应该是先对我们的传参作一些封装处理(包含sql语句、传参、传参类型等等)。然后在创建了一个为查询或者保存缓存用的CacheKey对象,这玩意儿有点长(方法id,sql语句,参数信息等等),感觉啥都包含了,就是为了确认查询的唯一性的。

2、之后先执行检查缓存中有没有,没有的话使用再调用被CacheExecutor包装的真正的Executor执行查询
query方法

3、从本地缓存中拿数据(一级缓存),没有的话再重新执行查询

4、后面一直跟踪到SimpleExecutor的执行器的doQuery方法(查询完了之后的结果又会存放在localCache本地缓存中)。首先定义了一个JDBC原生的Statemten对象,也说明了底层就是根据JDBC完成的。此外,这个方法出现了非常重要的第二个接口对象StatementHandler(可使用拦截器拦截)。(注:在创建StatementHandler的时候,构造器中会默认创建另外两个重要对象PrepareHandler和ResultSethandler (BaseStatementHandler 抽象类中实现的))

StatementHandler ---> RoutingStatementHandler --> 默认PreparedStatementHandler

5、在上图中,使用prepareStatement()创建Statement对象中,需要先创建链接,然后使用RoutingStatementHandler进行参数预编译。这个预编译又依赖第三个特殊对象PrepareHandler对象进行辅助。 而PrepareHandler 又使用TypeHandler 进行参数的设置。

6、第四点中的图片,执行最后一步query后,返回的结果,又需要第四个非常重要的对象去处理返回的结果(方法在PreparedStatementHandler中),同样也用了TypeHandler辅助执行。

文中的四大对象Executor、StatementHandler、PrepareHandler和ResultSetHandler 都有一句interceptorChain.pluginAll(target)的方法用于包装它们,这也是实现Mybatis的Plugin插件的切入点。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,588评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,456评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,146评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,387评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,481评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,510评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,522评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,296评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,745评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,039评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,202评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,901评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,538评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,165评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,415评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,081评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,085评论 2 352

推荐阅读更多精彩内容