上篇文章讲了mybatis是怎么创建SqlSessionFactory的:mybatis源码解析-SqlsessionFactory,简单来说就是通过构造模式读取xml配置文件封装到Configuration
对象中,返回一个默认的DefaultSqlSessionFactory
对象,需要注意的是我们写的每一个mapper映射文件在配置类中定义好了之后会被解析进Configuration
中,并且mapper映射文件中的增删改查标签都有与之对应的MappedStatement
对象封装,我们还是摆出hello world来演示:
//1.通过输入流解析xml配置文件
InputStream inputstream = Resources.getResourceAsStream("xxx.xml")
SqlsessionFactory sqlsessionfactory = new SqlsessionFactoryBuilder().build(inputstream);
//2.获取和数据库的链接,创建会话
SqlSession openSession = sqlsessionfactory.openSession();
//3.获取接口的实现类对象,会为接口自动的创建一个代理对象mapper,代理对象会去执行增删改查方法
xxxMapper mapper = openSession.getMapper(xxxMapper.class)
//4.执行增删改的方法
第一步已经讲过了,现在来看第二步,获取链接创建会话SqlSession
Sqlsession
1.1:直接来到sqlsessionfactory.openSession();
这个方法,它其实调用的是DefaultSqlSessionFactory
的openSessionFromDataSource
方法
public SqlSession openSession(boolean autoCommit) {
return this.openSessionFromDataSource(this.configuration.getDefaultExecutorType(), (TransactionIsolationLevel)null, autoCommit);
}
在进入查看这个方法之前,this.configuration.getDefaultExecutorType()
这个是选择一种执行器
这里默认选的是SIMPLE,在mybatis官方文档有这样的三种设置:
1.2:好了,进入openSessionFromDataSource
这个方法:
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
DefaultSqlSession var8;
try {
Environment environment = this.configuration.getEnvironment();
TransactionFactory transactionFactory = this.getTransactionFactoryFromEnvironment(environment);
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
Executor executor = this.configuration.newExecutor(tx, execType);
var8 = new DefaultSqlSession(this.configuration, executor, autoCommit);
} catch (Exception var12) {
this.closeTransaction(tx);
throw ExceptionFactory.wrapException("Error opening session. Cause: " + var12, var12);
} finally {
ErrorContext.instance().reset();
}
return var8;
}
首先根据configuration
获取当前环境,配置事务信息,Executor executor = this.configuration.newExecutor(tx, execType);
这里根据我们选择的执行器类型创建执行器,进入newExecutor
方法:
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
executorType = executorType == null ? this.defaultExecutorType : executorType;
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
Object 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);
}
if (this.cacheEnabled) {
executor = new CachingExecutor((Executor)executor);
}
Executor executor = (Executor)this.interceptorChain.pluginAll(executor);
return executor;
}
那么什么是Executor
呢?请往下看:
这个接口里的方法就是我们执行增删改查的方法,我们继续走,在newExecutor
方法中有这样一段代码:
if (this.cacheEnabled) {
executor = new CachingExecutor((Executor)executor);
}
它的意思是判断cacheEnabled
是否开启也就是我们configuration
全局配置中是否开启二级缓存,如果开启就用CachingExecutor((Executor)executor)
来包装这个执行器,我们进入这个类:
1.3:我们截取一部分代码来研究:
public class CachingExecutor implements Executor {
private final Executor delegate;
private final TransactionalCacheManager tcm = new TransactionalCacheManager();
public CachingExecutor(Executor delegate) {
this.delegate = delegate;
delegate.setExecutorWrapper(this);
}
来看一看这个类的结构:、
我们可以知道,这个类将传进来的执行器包装了一下,真正执行增删改查的是他内部的Executor delegate
对象,我们继续看newExecutor
在最终完成并返回Executor
前,还会调用这个方法:
Executor executor = (Executor)this.interceptorChain.pluginAll(executor);
就是调用拦截器链的pluginAll方法,在目前为止这个方法看起来貌似没什么作用,其实不然,在后面的插件介绍中,它就通过这种方式来包装我们的执行器,我们来看一下这个方法:
public Object pluginAll(Object target) {
Interceptor interceptor;
for(Iterator var2 = this.interceptors.iterator(); var2.hasNext(); target = interceptor.plugin(target)) {
interceptor = (Interceptor)var2.next();
}
return target;
}
很简单,调用所有的拦截器来对Executor进行封装,好了,到现在我们就把Executor封装完毕了,回到1.2的openSessionFromDataSource
这个方法:
Executor executor = this.configuration.newExecutor(tx, execType);
var8 = new DefaultSqlSession(this.configuration, executor, autoCommit);
return var8;
将executor
传入了DefaultSqlSession
中,来看一下这个类:
最终返回了DefaultSqlSession
总结
最后用一张图来简单回顾一下: