Mybatis全方位剖析【三】——Mybatis工作原理分析
一、Mybatis的工作流程
通过上一节我们了解到了MyBatis的基本使用,这一节来看一下MyBatis的体系结构以及工作原理,我们首先来再看一下MyBatis的工作流程
首先MyBatis启动的时候回去解析核心配置文件以及Mapper.xml映射文件,MyBatis会将我们配置文件中的所有信息都封装保存在全局的Configuration中,接下来我们操作数据库需要与数据库之间获得会话,即获得SqlSession对象,在获取SqlSession对象的时候MyBatis通过SqlSessionFactoryBuilder来构建出了工厂对象SqlSessionFactory对象,通过SqlSessionFactory会话工厂来获取SqlSession,获取与数据库之间的连接。在SqlSession中持有了Executor对象,用来对数据库进行操作,在Executor执行数据库操作的时候,会使用ParameterHandler来对参数进行操作,使用StatementHandler与数据库进行交互,使用ResultSetHandler来对获取的结果集进行处理。
二、Mybatis的工作原理
接下来以一个MyBatis编程式查询demo来进行分析:
@Test
public void testQueryById() throws IOException {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession session = sqlSessionFactory.openSession();
try {
BlogMapper mapper = session.getMapper(BlogMapper.class);
Blog blog = mapper.selectBlogById(1);
System.out.println(blog);
} finally {
session.close();
}
}
1.SqlSessionFactory的构建
从上面demo可以看出大致分为四步,第一步通过SqlSessionFactoryBuilder构建SqlSessionFactory,来具体看看MyBatis在做了什么:
当我们在使用SqlSessionFactoryBuilder的build方法 构建 SqlSessionFactory 的时候,会对Mybatis的核心配置文件进行解析
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
在SqlSessionFactoryBuilder的build方法中会使用 XMLConfigBuilder 的 parse()方法对配置文件进行解析
public class SqlSessionFactoryBuilder {
// ....
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
return build(parser.parse());
//.....
}
在 parse()方法中,parseConfiguration()方法会选取configuration根标签开始解析
public Configuration parse() {
if (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
parsed = true;
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
parseConfiguration()方法对核心配置文件中的一级标签进行解析
private void parseConfiguration(XNode root) {
try {
// issue #117 read properties first
/*
properties标签:properties标签用于配置参数信息,比如最常见的数据库连接信息,在xml只需要${}引用就可以了
properties属性:
resource: resource 是相对路径
url: url指定本地服务器或者网络的绝对路径
*/
propertiesElement(root.evalNode("properties"));
/*
settings标签:MyBatis的核心配置,其<settings>标签也解析成了一个 Properties 对象,对于<settings> 标签的子标签的处理在后面,
先解析成Properties是因为后面两个方法需要使用
*/
Properties settings = settingsAsProperties(root.evalNode("settings"));
/*
读取本地文件或FTP文件的时候,需要用到VFS(Vitual File System)类
*/
loadCustomVfs(settings);
/*
指定 MyBatis 所用日志的具体实现,未指定时将自动查找。
*/
loadCustomLogImpl(settings);
/*
typeAliases标签:
TypeAliases是类型的别名,用来简化全路径类名的拼写,像我们的参数类型和返回值都可能会用到,在MyBatis中预定义的类型别名,在TypeAliasesRegistry中
*/
typeAliasesElement(root.evalNode("typeAliases"));
/*
plugins标签:
解析插件,植入插件逻辑,标签解析完之后会生成一个Interceptor对象,并且添加到Configuration的InterceptorChain 属性里面,它是一个 List
*/
pluginElement(root.evalNode("plugins"));
/*
objectFactory标签:
MyBatis在将数据库中的数据转换为java对象的时候,会通过ObjectFactory创建对象,我们可以自定义ObjectFactory,让MyBatis执行我们的创建对象逻辑
*/
objectFactoryElement(root.evalNode("objectFactory"));
/*
用来实例化对象
*/
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
/*
解析 reflectorFactory 标签,生成 ReflectorFactory 对象
*/
reflectorFactoryElement(root.evalNode("reflectorFactory"));
/*
处理<settings>标签里面的所有子标签,子标签属性的默认值都是在这里赋值的,都会赋值到Configuration的相应属性里面去
*/
settingsElement(settings);
// read it after objectFactory and objectWrapperFactory issue #631
/*
一个 environment 就是对应一个数据源,所以在这里我们会根据配 置的<transactionManager>创建一个事务工厂,
根据<dataSource>标签创建一个数据源,最后把这两个对象设置成 Environment 对象的属性,放到 Configuration 里面
*/
environmentsElement(root.evalNode("environments"));
/*
解析 databaseIdProvider 标签,生成 DatabaseIdProvider 对象(用来支持不同厂商的数据库
*/
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
/*
解析typeHandlers,用来做数据库和Java对象之间的相互转换,最后TypeHandler会注册到TypeHandlerRegistry
*/
typeHandlerElement(root.evalNode("typeHandlers"));
/*
解析<mappers>标签,并解析Mapper.xml映射文件中的所有子标签,将每一个操作数据库的元素都解析为一个个MappedStatement对象,并且将
Mapper接口的Class<T>类和工厂类进行了绑定,同时会解析Mapper接口方法上的注解,最后同样会解析成一个个MappedStatement对象,注解
和Mapper.xml配置文件作用相同,
*/
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
到这里所有配置文件,包括Mapper接口上的注解解析完成,全部设置到了Configuration对象里面
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
最后返回一个DefaultSqlSessionFactory对象,到这里第一步SqlSessionFactory的构建就完成了
2.SqlSession的构建
接下来看第二步SqlSession的构建,我们跟数据库的每一次链接,都需要创建一个会话,我们用openSession()方法来创建
public class DefaultSqlSessionFactory implements SqlSessionFactory {
@Override
public SqlSession openSession() {
return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
}
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
try {
final Environment environment = configuration.getEnvironment();
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
//在核心配置文件中transactionManager 配置的是JDBC则创建JdbcTransactionFactory,配置为MANAGED则创建ManagedTransactionFactory
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
/*
读取<setting/>标签中defaultExecutorType配置的值,可配置为 SIMPLE、REUSE、BATCH,默认为SIMPLE, 创建 SimpleExecutor 执行器
SIMPLE:普通的执行器 对应 SimpleExecutor
REUSE:执行器会重用预处理语句(PreparedStatement) 对应 ReuseExecutor
BATCH:执行器不仅重用语句还会执行批量更新 对应 BatchExecutor
*/
final Executor executor = configuration.newExecutor(tx, execType);
return new DefaultSqlSession(configuration, executor, autoCommit);
} catch (Exception e) {
closeTransaction(tx);
throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
如果在核心配置文件中配置了cacheEnabled=ture,会用装饰器模式对 executor 进行包装:new CachingExecutor(executor)
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
executorType = executorType == null ? defaultExecutorType : executorType;
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
Executor executor;
if (ExecutorType.BATCH == executorType) {
executor = new BatchExecutor(this, transaction);
} else if (ExecutorType.REUSE == executorType) {
executor = new ReuseExecutor(this, transaction);
} else {
executor = new SimpleExecutor(this, transaction);
}
//用CachingExecutor装饰Executor
if (cacheEnabled) {
executor = new CachingExecutor(executor);
}
//责任链模式注册所有插件
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
到这里SqlSession会话对象已经创建完成,最后获得了一个DefaultSqlSession,里面包含了一个 Executor,它是 SQL 的执行者。
3.获取Mapper对象
现在我们已经得到了 DefaultSqlSession,并且也有了Sql执行器Executor,所有我们必须要能够找到Mapper.xml里面定义的Statement Id 才能执行对应的Sql语句,在MyBatis中,找到 Statement ID 有两种方式:一种是直接调用 session 的方法,在参数里面传入 Statement ID,这种方式属于硬编码,我们没办法知道有多少处调用,修改起来也很麻烦,另一种是定义一个接口,然后再调用 Mapper 接口的方法。由于我们的接口名称跟 Mapper.xml 的 namespace 是对应的,接口的方法跟 statement ID 也都是对应的,所以根据方法就能找到对应的要执行的 Sql。接下来我们来具体看一下是怎么获取Mapper对象的:在上面demo代码中调用了session.getMapper(Class<T> type)
方法
BlogMapper mapper = session.getMapper(BlogMapper.class);
public class DefaultSqlSession implements SqlSession {
//.....
@Override
public <T> T getMapper(Class<T> type) {
return configuration.getMapper(type, this);
}
//.....
}
public class Configuration {
//.....
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
return mapperRegistry.getMapper(type, sqlSession);
}
//.....
}
传入Class接口的Class对象,获取到MapperProxyFactory工厂对象,调用了它的newInstance()方法
public class MapperRegistry {
//....
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
//这里knownMappers是在初始化Configuration对象的时候解析注册进去的
final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
}
try {
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}
//....
}
这里newInstance方法中使用了JDK的代理模式,这里的MapperProxy就是实现了InvocationHandler的触发管理类
public class MapperProxyFactory<T> {
protected T newInstance(MapperProxy<T> mapperProxy) {
/**
* @Description: 使用JDK动态代理创建Mapper代理对象
* @param ClassLoader
* @param interfaces
* @param InvocationHandler 实现了InvocationHandler接口的触发管理类
* @return Mapper代理对象
* @Author zdp
* @Date 2022-01-04 11:18
*/
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
public T newInstance(SqlSession sqlSession) {
//创建MapperInterface对应的MapperProxy 对象,比如mapperInterface是 BlogMapper.class,就创建MapperProxy
final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
}
到这里也解释了为什么接口要绑定一个MapperProxyFactory工厂对象,这个工厂类的作用就是创建Mapper代理对象的,但是在MyBatis中的代理和平时的JDK代理有点不同,JDK的动态代理中,在实现了 InvocationHandler 的代理类里面,需要传入一个被代理对象的实现类,而MyBatis中并不需要实现类,因为MyBatis并不是真正要增强被代理对象,而是只需要根据接口类型+方法的名称,就可以找到 Statement Id,就可以执行Sql了。到这里已经得到了一个Mapper的代理对象,接下来看看Sql是怎么执行的
4.执行Sql
因为所有的Mapper对象都是通过MapperProxy代理出来的,所以当我们在执行Mapper方法的时候,都会去执行到Mapper的invoke()方法
public class MapperProxy<T> implements InvocationHandler, Serializable {
//.....
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
// 如果是Object的方法就放行
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
} else {
//像上面调用demo中的 selectBlogById 方法都会在这里开始执行
return cachedInvoker(method).invoke(proxy, method, args, sqlSession);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
//.....
}
这里先从缓存methodCache<K,V>中去根据 method (method就是缓存中的Key) 获取,如果没有获取到MapperMethodInvoker,则执行 m -> {}这个自定义的实现,返回自定义的MapperMethodInvoker
private MapperMethodInvoker cachedInvoker(Method method) throws Throwable {
try {
return MapUtil.computeIfAbsent(methodCache, method, m -> {
//判断是否是默认方法
if (m.isDefault()) {
try {
if (privateLookupInMethod == null) {
return new DefaultMethodInvoker(getMethodHandleJava8(method));
} else {
return new DefaultMethodInvoker(getMethodHandleJava9(method));
}
} catch (IllegalAccessException | InstantiationException | InvocationTargetException
| NoSuchMethodException e) {
throw new RuntimeException(e);
}
} else {
//这里new MapperMethod()的时候会去初始化SqlCommand,会根据传入的method拿到执行方法的名称,通过mapperInterface拿到接口名称,通过手动拼接的方式拿到StatementId,然后根据StatementId去Configuration中获取Ms对象,
return new PlainMethodInvoker(new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()));
}
});
} catch (RuntimeException re) {
Throwable cause = re.getCause();
throw cause == null ? re : cause;
}
}
public class MapperMethod {
/**
* @Description: MapperMethod构造方法
* @Author zdp
* @Date 2022-01-07 14:25
*/
public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
this.command = new SqlCommand(config, mapperInterface, method);
this.method = new MethodSignature(config, mapperInterface, method);
}
}
public static class SqlCommand {
public SqlCommand(Configuration configuration, Class<?> mapperInterface, Method method) {
final String methodName = method.getName();
final Class<?> declaringClass = method.getDeclaringClass();
//获取ms对象
MappedStatement ms = resolveMappedStatement(mapperInterface, methodName, declaringClass,configuration);
if (ms == null) {
if (method.getAnnotation(Flush.class) != null) {
name = null;
type = SqlCommandType.FLUSH;
} else {
throw new BindingException("Invalid bound statement (not found): "
+ mapperInterface.getName() + "." + methodName);
}
} else {
//这里name就是获取的StatementId = namespace + methodName
name = ms.getId();
type = ms.getSqlCommandType();
if (type == SqlCommandType.UNKNOWN) {
throw new BindingException("Unknown execution method for: " + name);
}
}
}
}
看下根据StatementId 获取ms对象的过程
private MappedStatement resolveMappedStatement(Class<?> mapperInterface, String methodName,
Class<?> declaringClass, Configuration configuration) {
//根据接口名 + 方法名 拼接得到 StatementId
String statementId = mapperInterface.getName() + "." + methodName;
if (configuration.hasStatement(statementId)) {
//从configuration获取ms对象,之前解析Mapper.xml文件的时候说了,Mapper.xml里面的像select、update等元素都会被解析为一个个ms对象保存在configuration中
return configuration.getMappedStatement(statementId);
} else if (mapperInterface.equals(declaringClass)) {
return null;
}
for (Class<?> superInterface : mapperInterface.getInterfaces()) {
if (declaringClass.isAssignableFrom(superInterface)) {
MappedStatement ms = resolveMappedStatement(superInterface, methodName,
declaringClass, configuration);
if (ms != null) {
return ms;
}
}
}
return null;
}
接着看invoke()方法这里我们会走到 PlainMethodInvoker调用其invoke方法
private static class PlainMethodInvoker implements MapperMethodInvoker {
private final MapperMethod mapperMethod;
public PlainMethodInvoker(MapperMethod mapperMethod) {
super();
this.mapperMethod = mapperMethod;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable {
return mapperMethod.execute(sqlSession, args);
}
}
接下来调用了execute()方法,这里我们以查询为例就只看查询的逻辑了,
public class MapperMethod {
public Object execute(SqlSession sqlSession, Object[] args) {
//....
switch (command.getType()) {
case SELECT:
//判断是否没有返回值, void.class.equals(this.returnType);
//
if (method.returnsVoid() && method.hasResultHandler()) {
executeWithResultHandler(sqlSession, args);
result = null;
//判断方法返回值是否是集合或者数组
} else if (method.returnsMany()) {
result = executeForMany(sqlSession, args);
//判断返回值是否是Map
} else if (method.returnsMap()) {
result = executeForMap(sqlSession, args);
//判断返回值是否是Cursor Cursor.class.equals(this.returnType)
} else if (method.returnsCursor()) {
result = executeForCursor(sqlSession, args);
} else {
//转换为sql执行参数
Object param = method.convertArgsToSqlCommandParam(args);
result = sqlSession.selectOne(command.getName(), param);
if (method.returnsOptional()
&& (result == null || !method.getReturnType().equals(result.getClass()))) {
result = Optional.ofNullable(result);
}
}
break;
//....
}
}
}
我们返回值是Blog对象,这里会走到else里面,可以看到执行了selectOne()方法,
public class DefaultSqlSession implements SqlSession {
@Override
public <T> T selectOne(String statement, Object parameter) {
// Popular vote was to return null on 0 results and throw exception on too many.
List<T> list = this.selectList(statement, parameter);
if (list.size() == 1) {
return list.get(0);
} else if (list.size() > 1) {
throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size());
} else {
return null;
}
}
//
private <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) {
try {
MappedStatement ms = configuration.getMappedStatement(statement);
return executor.query(ms, wrapCollection(parameter), rowBounds, handler);
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
}
最终selectOne方法还是调用的selectList方法,到这里我们是不是可以看到一段熟悉的异常,当查询返回一个对象的时候,却查询到了多条数据,就会抛出TooManyResultsException异常,我们接着看selectList()方法最后是使用executor调用了query()方法 ,这里我们没有去关闭二级缓存,那么其实会使用CachingExecutor 调用其 query()方法,若是关闭了二级缓存则会走到BaseExecutor 的 query()方法
public class CachingExecutor implements Executor {
@Override
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
//获取执行Sql
BoundSql boundSql = ms.getBoundSql(parameterObject);
//创建缓存Key,后续若是再次查询,则通过该值获取结果
CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
@Override
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
throws SQLException {
Cache cache = ms.getCache();
if (cache != null) {
flushCacheIfRequired(ms);
if (ms.isUseCache() && resultHandler == null) {
ensureNoOutParams(ms, boundSql);
@SuppressWarnings("unchecked")
List<E> list = (List<E>) tcm.getObject(cache, key);
if (list == null) {
list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
tcm.putObject(cache, key, list); // issue #578 and #116
}
return list;
}
}
return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
}
上面通过ms获取了一个缓存对象,这里我们没有在Mapper.xml映射文件中配置<cache/>
缓存标签(不清楚MyBatis缓存的可以看这),这里的cache为空,直接走下面的query()方法
public abstract class BaseExecutor implements Executor {
@Override
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
//....
//ms.isFlushCacheRequired() 判断的是 Select 元素的flushCache值是否为true, select默认为false
if (queryStack == 0 && ms.isFlushCacheRequired()) {
//清空缓存
clearLocalCache();
}
List<E> list;
try {
queryStack++;
//通过缓存Key先从缓存获取
list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
if (list != null) {
handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
} else {
//没有缓存则从数据库查询
list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
} finally {
queryStack--;
}
//....
return list;
}
}
从数据库查询
private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
List<E> list;
//先占位
localCache.putObject(key, EXECUTION_PLACEHOLDER);
try {
//从数据库查询数据
list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
} finally {
//移除占位符
localCache.removeObject(key);
}
//缓存数据
localCache.putObject(key, list);
if (ms.getStatementType() == StatementType.CALLABLE) {
localOutputParameterCache.putObject(key, parameter);
}
return list;
}
doQuery()看看MyBatis从数据库查询数据的过程,在这里创建了StatementHandler,这里StatementHandler实例的创建取决于在Mapper.xml映射文件中Select元素配置的statementType类型,默认是PREPARED,所以这里创建的handler实例是 PreparedStatementHandler
public class SimpleExecutor extends BaseExecutor {
@Override
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
Statement stmt = null;
try {
Configuration configuration = ms.getConfiguration();
//
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
stmt = prepareStatement(handler, ms.getStatementLog());
return handler.query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
}
创建StatementHandler 的同时也创建了处理参数的ParameterHandler 和处理结果集的ResultSetHandler
public abstract class BaseStatementHandler implements StatementHandler {
protected BaseStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
//......
this.parameterHandler = configuration.newParameterHandler(mappedStatement, parameterObject, boundSql);
this.resultSetHandler = configuration.newResultSetHandler(executor, mappedStatement, rowBounds, parameterHandler, resultHandler, boundSql);
}
}
完成StatementHandler 的创建之后,调用prepareStatement(handler, ms.getStatementLog());方法对语句进行预编译,处理参数,最后调用PreparedStatementHandler的query()方法
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
//这里的execute就是JDBC包中的方法了
ps.execute();
//调用DefaultResultSetHandler进行结果集处理
return resultSetHandler.handleResultSets(ps);
}
到这里一个简单的查询流程就完成了,最后用一句话简单总结一下整个流程:
首先MyBatis会去加载核心配置文件,Mapper.xml映射文件,将所有的配置信息都会封装到全局的Configuration对象中,为了与数据库进行交互需要SqlSession,在此之前通过SqlSessionFactoryBuilder构建出会话工厂SqlSessionFactory会话工厂,通过会话工厂拿到SqlSession对象,同时也创建出了Executor执行器,之后通过Mapper接口绑定的MapperProxyFactory工厂对象获得代理的Mapper对象,然后通过Mapper的namespace+method作为StatementId,从Configuration对象中拿到MappedStatement 对象,从MappedStatement 对象拿到执行Sql,之后通过ParameterHandler 处理Sql参数,通过StatementHandler执行Sql语句,最后ResultSetHandler处理结果集。
以上就是MyBatis工作原理的简单介绍了,如有错误希望你指正,希望能对你有点帮助!