看完sql生成过程,我们再来看看mapper接口的实例化。众所周知,接口是无法实例化的,那Mybatis到底实例出来的是什么东西呢?
mapper的实例化通过SqlSession获取的,因此我们先来看看SqlSession的创建过程:
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
try {
final Environment environment = configuration.getEnvironment();
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
final Executor executor = configuration.newExecutor(tx, execType);
return new DefaultSqlSession(configuration, executor, autoCommit);
} catch (Exception e) {
closeTransaction(tx); // may have fetched a connection so lets call close()
throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
可以看到Executor也在这里实例化了,还记得Executor是真正发起数据库操作的执行器,并且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);
}
if (cacheEnabled) {
executor = new CachingExecutor(executor);
}
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
发现Executor一共四个实现类:BatchExecutor、ReuseExecutor、SimpleExecutor、CachingExecutor,其用途看名字基本可以猜出来:
- SimpleExecutor:常规执行器,每次执行都会创建一个statement,用完后关闭。
- ReuseExecutor:可重用执行器,将key=sql,value=statement存入map中,操作map中的statement而不会重复创建statement。
- BatchExecutor:批处理型执行器,doUpdate预处理存储过程或批处理操作,doQuery提交并执行过程。
- CachingExecutor: 缓存执行器,用于包装以上三个执行器,缓存 statement的id、查询offset/limit、sql、参数和环境id,属于二级缓存(此二级非二级分布式缓存,它由事务缓存管理器提供,仍然存储于本地)。
另外,我们还留意到interceptorChain.pluginAll(executor);,看起来跟web的filter链十分相似,其实内部的确是一条责任链:
public Object pluginAll(Object target) {
for (Interceptor interceptor : interceptors) {
target = interceptor.plugin(target);
}
return target;
}
通过查看其调用链,我们发现不仅executor调用了:
这是Mybatis的一种扩展机制,通过拦截器添加自定义操作,需要实现各自的接口并实现到全局配置中。
注意: Inteceptor是使用JDK的动态代理来实现的,所以它只能对接口进行拦截
executor创建完成后,将其与Configuration一起传入DefaultSqlSession,至此SqlSession创建完成。接下来利用SqlSession.getMapper实例化mapper,实际上是通过mapperRegistry进行。还记得mapperRegistry一开始在解析mapper时创建了 Mapper代理工厂类MapperProxyFactory,这个工厂类同样是使用了JDK的动态代理来创建代理类MapperProxy:
protected T newInstance(MapperProxy<T> mapperProxy) {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
public T newInstance(SqlSession sqlSession) {
final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
当调用mapper方法时,就会进入其实现了InvocationHandler的invoke方法,之后的流程在 从源码看世界:Mybatis一次数据库操作过程 已说明,这里不再叙述。