目录
1. 目标
通过本篇文章,你可以如下问题的答案。springboot启动过程中,mybatis都做了什么?执行具体方法时,mybatis都做了什么?
2. 启动过程
只要是能和spring进行组合第三方中间件,都是针对spring扩展点进行个性化实现。关于mybatis的启动过程,需要先了解springboot的启动流程,如果还不了解,请前往另一篇文章。
2.1. 启动过程经历的几个流程
2.2. 对每个流程进行概述
2.2.1. beanfactory生命周期扩展,扫描mapper接口并注册到工厂
在beanFactoryPostProcessor回调时
依据MapperScan配置,扫描生成mapper接口的BeanDefinition,每个BeanDifinition的beanClass均设置为org.mybatis.spring.mapper.MapperFactoryBean,作为真正待实例化的类,然后注册到beanfactory。
org.mybatis.spring.annotation.MapperScannerRegistrar#registerBeanDefinitions
org.mybatis.spring.mapper.ClassPathMapperScanner
2.2.2. beanfactory生命周期扩展,autoConfig自定义
mybatis包中spring.factories配置
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration
autoconfig会在scan过程执行后执行,即所有用户定义的bean都注册到工厂后执行。
mybatis检查mapper xml资源是否存在,生成SqlSessionFactory和SqlSessionTemplate。
2.2.3. bean生命周期扩展,匹配解析xml与配置
在beanPostProcessor.after回调时匹配解析对应的xml
org.springframework.dao.support.DaoSupport#afterPropertiesSet
org.mybatis.spring.mapper.MapperFactoryBean#checkDaoConfig
org.apache.ibatis.binding.MapperRegistry#addMapper
org.apache.ibatis.builder.annotation.MapperAnnotationBuilder#parse
匹配mapper.xml:mapper类全路径.replace('.', '/') + ".xml"
解析xml:每个method生成一个methodStatement,主要是通过注解添加的配置,如cache配置
2.2.4. bean生命周期扩展,生成mapperProxy
在beanPostProcessor.after回调时每个mapper实例生成一个mapperProxy
org.apache.ibatis.binding.MapperProxy
mapper接口引用
org.apache.ibatis.binding.MapperProxy#mapperInterface
mapper接口中定义的方法与mapper.xml配置关系
org.apache.ibatis.binding.MapperProxy#methodCache
3. 执行过程
通过参数解析器、执行器,执行具体mapper method。具体流程如下:
mapperProxy找到对应的或生成新的mapperMethod,涉及解析方法签名,如返回类型、定义的入参等 |
mapperMethod根据sql类型选择处理策略,即insert\update\delete\select,以select为例 |
组织入参,放于map中。 map-key包括:
|
调用sqlSession实例(如sqlSessionTemplate)对应方法,如selectList |
委派给DefaultSqlSession#selectList |
委派给执行器#query |
拦截器intercepter,可以修改sql,如分页处理、数据隔离等 |
实际获取数据,使用cache或从数据库查询 |
结果处理,根据定义的返回值类型,决定直接返回,或组织为数组或集合。 |
MyBatis 利用本地缓存机制(Local Cache)防止循环引用和加速重复的嵌套查询。 默认值为 SESSION,会缓存一个会话中执行的所有查询。 若设置值为 STATEMENT,本地缓存将仅用于执行语句,对相同 SqlSession 的不同查询将不会进行缓存。
3.1. 执行mapper具体方法
org.apache.ibatis.binding.MapperMethod#execute
3.2. 参数解析器
根据xml配置参数与当前参数生成参数关系
3.3. 执行器执行
根据之前解析的xml配置与当前参数生成变量绑定的sql(org.apache.ibatis.mapping.MappedStatement#getBoundSql)
若使用cache则从cache取结果并返回
不使用cache或cache未命中则执行sql
3.4. 结果转换
4. 主要类的生命周期和应用范围
MyBatis中常用的类就要数SqlSessionFactoryBuilder、SqlSessionFactory、SqlSession、SqlMapper了。那么下面对他们的应用范围和生命周期进行一下说明:
SqlSessionFactoryBuilder | 在应用中该类实例的主要作用是创建SqlSessionFactory实例,故任务完成后就可以消失了。因此该类实例的最佳应用范围和生命周期应为“方法范围”。 |
SqlSessionFactory | 在应用的整个周期中会有众多地方需要利用其实例打开某个SqlSession,因此最佳范围是“应用生命周期范围”。故此,可以使用单例与工厂模式,在官方文档中最佳建议是IoC容器,如Spring来生成该实例。 |
SqlSession | 该类是非线程安全的,其实例是不能共享的,所以应该每个线程具有自己的SqlSession实例。因此最佳建议是“请求或方法范围”。例如:收到一个Http请求后,创建一个实例,进行某些操作,之后关闭。确保将关闭放于finally中。 |
SqlMapper | 是创建绑定映射语句的接口。其实例从SqlSession获得,所以其最宽生命周期与SqlSession相同,因此其实例的执行范围也是“方法范围”,而且其不需要明确的关闭。 |