环境:
1 | mybatis-spring-boot-starter: 1.3.4 |
在MybatisAutoConfiguration
类中查看Mybatis的SqlSessionFactory
接口的实现DefaultSqlSessionFactory
是如何注册到Spring容器的。
由于不是本文重点,这里就不贴源码了,描述下大概流程:
Mybatis是通过SqlSessionFactoryBean
工厂bean的方式获取bean实例的。
首先通过SqlSessionFactoryBean
类的buildSqlSessionFactory
方法中调用xmlMapperBuilder.parse()
方法
将Mapper
接口的Class类型作为key,MapperProxyFactory
实例作为value注册到MapperRegistry
类上。
然后将MapperRegistry
实例设置到Configuration
类的mapperRegistry
属性上。
最后调用SqlSessionFactoryBuilder
类的build
方法完成SqlSessionFactory
实例的创建:
1 | public SqlSessionFactory build(Configuration config) { |
这里看下MapperProxyFactory
类的源码:
1 | public class MapperProxyFactory<T> { |
可以看到通过调用MapperProxyFactory
的newInstance
方法完成Mapper
接口代理的生成。这里用到了Jdk的动态代理。
这里说明一点,Mybatis和Spring整合使用SqlSession
的实现是SqlSessionTemplate
,它是线程安全的由Spring管理的单例bean。
当调用Mapper
接口的方法时,实际上会调用MapperProxy
代理的invoke
方法:
1 | @Override |
然后返回调用MapperMethod
类的execute
方法:
1 | public Object execute(SqlSession sqlSession, Object[] args) { |
可以看到这里通过命令模式处理对应的Sql。这里笔者选择执行一条查询命令,那么最终会调用该类的executeForMany
方法:
1 | private <E> Object executeForMany(SqlSession sqlSession, Object[] args) { |
接下来进入SqlSessionTemplate
的selectList
方法:
1 | @Override |
可以看到其实是调用的SqlSession
的代理来调用实际的selectList
方法。
查看SqlSessionTemplate
类的构造方法的源码有这一行:
1 | this.sqlSessionProxy = (SqlSession) newProxyInstance(SqlSessionFactory.class.getClassLoader(), |
可知SqlSessionTemplate
持有SqlSession
接口的代理,其默认实现是DefaultSqlSession
。查看该类的selectList
方法实现:
1 | @Override |
由于Mybatis默认是打开本地缓存的,即每个Session都持有Executor
的引用实现CachingExecutor
。
接下来进入CachingExecutor
的query
方法:
1 | @Override |
进入BaseExecutor
的query方法实现,有这么一行:
1 | list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql); |
继续查看queryFromDatabase方法实现,有这么一行:
1 | list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql); |
由于默认使用Mybatis的ExecutorType是SIMPLE
,所以进入SimpleExecutor
的doQuery方法实现:
1 | @Override |
第一点,查看实现会返回RoutingStatementHandler
实例,由于默认的语句类型是StatementType.PREPARED
,
所以该类持有PreparedStatementHandler
对象的引用。
第二点,prepareStatement
方法实现:
1 | private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException { |
PreparedStatementHandler的parameterize方法会调用DefaultParameterHandler
的setParameters
方法处理参数映射:
1 | @Override |
第三点,委托调用PreparedStatementHandler
的query方法:
1 | @Override |
这里重点看下结果的映射,结果的类型处理和参数的类型处理都是基于TypeHandler
接口的实现,可以看看其抽象类的实现BaseTypeHandler
:
1 | @Override |
这里getNullableResult
方法是抽象方法,将由具体的子类实现。比如有个实体的属性是
1 | private LocalDateTime createAt; |
那么将会使用LocalDateTimeTypeHandler
类的实现,也可以指定自己的TypeHandler实现,在<resultMap>
标签
的子标签<result>
自定义typeHandler:
1 | <result property="createAt" column="create_at" typeHandler="org.apache.ibatis.type.LocalDateTimeTypeHandler"/> |
到此,Mybatis的调用流程源码分析结束。
以上,如有问题欢迎提出!