Mybatis流程學習總結(待續)
這幾天自己做小專案練手的時候對攔截器如何精準的攔截到selectbypage產生了好奇,進而研究了一下Mybatis的原始碼,瞭解了大致的流程。將幾個自己的疑問解答了一下。文章最下面畫了一幅流程圖,圖有點醜,湊合看吧。。
部分內容參考http://blog.csdn.net/ABCD898989/article/details/51261163
Question1:哪些類可以被攔截?
函式內部執行interceptorChain.pluginAll的類
public Executor newExecutor(Transaction transaction, ExecutorType executorType) { .....
//.....
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
在mybatis中一共有四個:parameterHandler 、resultSetHandler 、statmentHandler
、Executor
InterceptorChain裡儲存了所有的攔截器,它在mybatis初始化的時候建立。上面這句程式碼的含義是呼叫攔截器鏈裡的每個攔截器依次對executor進行plugin,程式碼如下:
public class InterceptorChain {
private final List<Interceptor> interceptors = new ArrayList<Interceptor>();
public Object pluginAll(Object target) {
for (Interceptor interceptor : interceptors) {
target = interceptor.plugin(target);
}
return target;
}
//...
}
Question2:為什麼分頁外掛是攔截statment的prepare(Connection connection)方法?
請看下面的程式碼,如果執行到List localList = handler.query(stmt, resultHandler);
這一步,那就執行了查詢,顯然要在這一步之前攔截。
那麼就選擇了攔截stmt = prepareStatement(handler, ms.getStatementLog());
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(this.wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
stmt = prepareStatement(handler, ms.getStatementLog());
List localList = handler.query(stmt, resultHandler);
return localList; } finally { closeStatement(stmt); } throw localObject;
}
可以執行攔截的類有這幾種parameterHandler 、resultSetHandler 、statmentHandler、Executor
所以,正好可以攔截Statement stmt = handler.prepare(connection);
這句話。
再進到prepareStatement裡看,這個handler.prepare(connection)。是不是很眼熟,分頁攔截器標準開頭。method指的就是prepare,args指的是裡面的引數型別
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException
{
Connection connection = getConnection(statementLog);
Statement stmt = handler.prepare(connection);
handler.parameterize(stmt);
return stmt;
}
@Intercepts({@Signature(type=StatementHandler.class,method="prepare",args={Connection.class})})
Question3:怎麼讓攔截器只處理分頁的sql語句?
先上段程式碼,攔截器的標準開頭,攔截器首先會攔截所有sql語句,具體判斷要依靠程式碼裡自己手動加條件判斷
@Intercepts({@Signature(type=StatementHandler.class,method="prepare",args={Connection.class})})
public class PageInterceptor implements Interceptor{
public Object intercept(Invocation arg0) throws Throwable {
StatementHandler statementHandler = (StatementHandler)arg0.getTarget();
MetaObject metaObject = MetaObject.forObject(statementHandler, SystemMetaObject.DEFAULT_OBJECT_FACTORY, SystemMetaObject.DEFAULT_OBJECT_WRAPPER_FACTORY,new DefaultReflectorFactory());
//MetaObject是Mybatis提供的一個的工具類,通過它包裝一個物件後可以獲取或設定該物件的原本不可訪問的屬性(比如那些私有屬性)
MappedStatement mappedStatement = (MappedStatement)metaObject.getValue("delegate.mappedStatement");
//你按照這個delegete的引數型別自定義一個處理方法,賦值過去,觸發這個delegete的時候,就會執行你新增的方法了
String id = mappedStatement.getId();
if(id.endsWith("ByPage")) {
這裡寫具體的分頁處理
}
}
}
就靠id.endsWith(“ByPage”)判斷,id我用systemout列印過,是包名+介面名+方法名。就是MyBatis Dao層的介面。下面的程式碼是我自己小專案的程式碼:
public interface AdDao {
List<Ad> selectByPage(Ad ad);
}
Qusetion4:為什麼要使用metaObject取值?
去BaseStatementHandler類裡面看,發現mappedStatement是protected,所以不能直接用get方法取出來,需要MetaObject的幫助,類似對映。
protected final MappedStatement mappedStatement
Question 5: metaObject.getValue()裡面的delegate.XXXX是什麼意思,為什麼要這麼寫
這個真不清楚。我猜一下,是通過metaObject.getValue()取值,通過delegate定位使用BaseStatementHandler 類中的引數。
public class RoutingStatementHandler implements StatementHandler {
......
private final StatementHandler delegate;
}
public abstract class BaseStatementHandler implements StatementHandler {
protected final Configuration configuration;
protected final ObjectFactory objectFactory;
protected final TypeHandlerRegistry typeHandlerRegistry;
protected final ResultSetHandler resultSetHandler;
protected final ParameterHandler parameterHandler;
protected final Executor executor;
protected final MappedStatement mappedStatement;
protected final RowBounds rowBounds;
protected BoundSql boundSql;
}
其實我具體往metaObject.getValue()的原始碼裡面看了一下,水還挺深的,最近沒時間研究這東西了,當做一個TODO項吧。
自己整理的MyBatis流程,稍微有一點亂