在上一篇文章中(原生Mybatis源碼簡析(上))巴帮,我們介紹了原生Mybatis的初始化,以及Mapper接口的運行原理侨赡。在介紹到Executor
類具體執(zhí)行SQL時摇庙,就沒有繼續(xù)下去了,現(xiàn)在我們繼續(xù)將剩下的流程梳理一下絮姆。
1醉冤、概述
在具體介紹代碼流程之前秩霍,我們得先介紹下如下四個比較重要的類,他在接下來的代碼分析中占有重要的地位蚁阳。
- Executor:Executor創(chuàng)建StatementHandler對象铃绒;
- StatementHandler: 設置sql語句,預編譯,設置參數(shù)等相關工作,以及執(zhí)行增刪改查方法;
- ParameterHandler: 設置預編譯參數(shù);
- ResultHandler: 處理查詢結果集;
其中在設置參數(shù)和處理查詢結果時,都是依賴TypeHandler,進行數(shù)據(jù)庫類型和javaBean類型的映射
2、流程介紹
從上篇文章中螺捐,我們知道颠悬,在獲取SqlSession的同時,其實已經(jīng)生成了Executor對象了归粉,并且是作為屬性設置給了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對象的實例化過程
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);
}
// 插件相關的代碼,先記住這里糠悼,后面分析Mybatis插件機制時届榄,再重點分析
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
這里executorType默認是simple,而Mybatis的二級緩存機制一般很少啟用(一般都是自己在業(yè)務層通過redis等實現(xiàn)緩存機制)倔喂,所以這里默認是返回SimpleExecutor類的實例铝条。
現(xiàn)在再回到上篇文章最后的代碼部分,DefaultSqlSession的selectList方法中席噩,調(diào)用了executor的query方法班缰。該方法會調(diào)用到BaseExecutor的query方法,不考慮一級緩存悼枢,最后會來到SimpleExecutor的doQuery方法
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);
}
}
在該方法中埠忘,具體SQL語句的執(zhí)行會有SimpleExecutor對象委托給StatementHandler對象處理。我們先看下該對象的實例化過程
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
// 這里是第二次出現(xiàn)關于插件的代碼馒索,后續(xù)會分析
statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
return statementHandler;
}
RoutingStatementHandler對象會也會根據(jù)statementType屬性實例化對應的StatementHandler對象
public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
switch (ms.getStatementType()) {
case STATEMENT:
delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
case PREPARED:
delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
case CALLABLE:
delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
default:
throw new ExecutorException("Unknown statement type: " + ms.getStatementType());
}
}
MappedStatement的StatementType屬性默認是PREPARED(可以在MappedStatement實例化方法中看到)莹妒,這里自然是實例化PreparedStatementHandler對象
protected BaseStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
this.configuration = mappedStatement.getConfiguration();
this.executor = executor;
this.mappedStatement = mappedStatement;
this.rowBounds = rowBounds;
this.typeHandlerRegistry = configuration.getTypeHandlerRegistry();
this.objectFactory = configuration.getObjectFactory();
if (boundSql == null) { // issue #435, get the key before calculating the statement
generateKeys(parameterObject);
boundSql = mappedStatement.getBoundSql(parameterObject);
}
this.boundSql = boundSql;
this.parameterHandler = configuration.newParameterHandler(mappedStatement, parameterObject, boundSql);
this.resultSetHandler = configuration.newResultSetHandler(executor, mappedStatement, rowBounds, parameterHandler, resultHandler, boundSql);
}
這里可以看出,在初始化StatementHandler的時候绰上,就會同時實例化parameterHandler和resultSetHandler旨怠,并設置為StatementHandler的屬性。
public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql);
// 這里是第三次出現(xiàn)關于插件的代碼蜈块,后續(xù)會分析
parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);
return parameterHandler;
}
public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, RowBounds rowBounds, ParameterHandler parameterHandler,
ResultHandler resultHandler, BoundSql boundSql) {
ResultSetHandler resultSetHandler = new DefaultResultSetHandler(executor, mappedStatement, parameterHandler, resultHandler, boundSql, rowBounds);
// 這里是第四次出現(xiàn)關于插件的代碼鉴腻,后續(xù)會分析
resultSetHandler = (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler);
return resultSetHandler;
}
我們再回到SimpleExecutor的doQuery方法中,先調(diào)用了prepareStatement方法百揭,完成SQL參數(shù)化配置
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
Connection connection = getConnection(statementLog);
stmt = handler.prepare(connection, transaction.getTimeout());
handler.parameterize(stmt);
return stmt;
}
然后調(diào)用了StatementHandler的query方法
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
ps.execute();
return resultSetHandler.handleResultSets(ps);
}
之后就是jdbc的套路了爽哎,就不再繼續(xù)分析了。下面再看下Mybatis的插件機制
3器一、插件機制
在上一篇文章中课锌,我們知道在初始化Configuration對象時,會初始化InterceptorChain對象盹舞,該對象中的interceptors屬性中保存了所有的插件對象产镐。在上面的代碼介紹了,我們一共發(fā)現(xiàn)了四處踢步,使用interceptorChain屬性的pluginAll方法癣亚,這也就是為什么Mybatis支持Executor、StatementHandler获印、ParamaterHandler述雾、ResultsetHandler四個對象的插件攔截機制的原因了。我們看下plugAll方法
public Object pluginAll(Object target) {
for (Interceptor interceptor : interceptors) {
target = interceptor.plugin(target);
}
return target;
}
很簡單兼丰,就是循環(huán)遍歷執(zhí)行Interceptor的plugin方法玻孟,而我們在實現(xiàn)一個插件時,通過繼承Interceptor接口鳍征,實現(xiàn)plugin方法都是如下實現(xiàn)方式黍翎。
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
該Plugin類時Mybatis提供的一個工具類
public static Object wrap(Object target, Interceptor interceptor) {
Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);
Class<?> type = target.getClass();
Class<?>[] interfaces = getAllInterfaces(type, signatureMap);
if (interfaces.length > 0) {
return Proxy.newProxyInstance(
type.getClassLoader(),
interfaces,
new Plugin(target, interceptor, signatureMap));
}
return target;
}
private static Map<Class<?>, Set<Method>> getSignatureMap(Interceptor interceptor) {
Intercepts interceptsAnnotation = interceptor.getClass().getAnnotation(Intercepts.class);
// issue #251
if (interceptsAnnotation == null) {
throw new PluginException("No @Intercepts annotation was found in interceptor " + interceptor.getClass().getName());
}
Signature[] sigs = interceptsAnnotation.value();
Map<Class<?>, Set<Method>> signatureMap = new HashMap<>();
for (Signature sig : sigs) {
Set<Method> methods = signatureMap.computeIfAbsent(sig.type(), k -> new HashSet<>());
try {
Method method = sig.type().getMethod(sig.method(), sig.args());
methods.add(method);
} catch (NoSuchMethodException e) {
throw new PluginException("Could not find method on " + sig.type() + " named " + sig.method() + ". Cause: " + e, e);
}
}
return signatureMap;
}
private static Class<?>[] getAllInterfaces(Class<?> type, Map<Class<?>, Set<Method>> signatureMap) {
Set<Class<?>> interfaces = new HashSet<>();
while (type != null) {
for (Class<?> c : type.getInterfaces()) {
if (signatureMap.containsKey(c)) {
interfaces.add(c);
}
}
type = type.getSuperclass();
}
return interfaces.toArray(new Class<?>[interfaces.size()]);
}
可見,Mybatis的插件機制是通過動態(tài)代理實現(xiàn)的艳丛,且多個插件會層層代理匣掸,代理的順序就是在xml中配置插件的順序。