GitHub地址
源碼解析地址
https://github.com/erlieStar/mybatis-3
debug源碼用項目
https://github.com/erlieStar/mybatis-examples
關於Mybatis的動態代理階段,主要涉及到4個組件
- Executor:一級緩存和二級緩存的實現都在Executor中
- StatementHandler:使用JDBC提供的Statement或PrepareStatement執行操作,起承上啓下的作用
- ParameterHandler:對預編譯的SQL進行參數設置
- ResultSetHandler:對數據庫返回的結果集(ResultSet)進行封裝,返回用戶指定的實體類型
這個動態代理階段執行的流程都包含在這張圖中,依次追一下代碼就能搞定
返回動態代理對象
用上一節的例子來debug動態代理的過程,一步一步追
sqlSession = sqlSessionFactory.openSession(true);
BookMapper bookMapper = sqlSession.getMapper(BookMapper.class);
int num = bookMapper.deleteById(1);
上一篇文章說到sqlSessionFactory的實現類是DefaultSqlSessionFactory,所以openSession返回的是DefaultSqlSession,追getMapper方法
追到了MapperRegistry類,這個類不是在初始化的時候保存了mapper接口對象和相應的MapperProxyFactory的映射關係嗎?
private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<Class<?>, MapperProxyFactory<?>>();
答對了,看來要從這放回mapper接口的代理對象了
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
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);
}
}
發現生成代理的工作都交給MapperProxyFactory類了,沒事,接着追
public T newInstance(SqlSession sqlSession) {
final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
protected T newInstance(MapperProxy<T> mapperProxy) {
// 實現了mapper接口的動態代理對象
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
到這終於追完了,對Proxy.newProxyInstance這個動態代理函數不瞭解的看如下文章
看Proxy.newProxyInstance()方法中的最後一個參數是啥?MapperProxy,原來mapper接口的代理類就是MapperProxy啊。
執行動態代理方法
當執行如下操作的時候,會跳進MapperProxy類的invoke()函數
int num = bookMapper.deleteById(1);
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 代理以後,所有Mapper方法調用時,都會執行這個invoke方法
try {
// 如果是Object本身的方法不增強
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
} else if (isDefaultMethod(method)) {
// 針對java7以上版本對動態類型語言的支持,暫不分析
return invokeDefaultMethod(proxy, method, args);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
// 從緩存中獲取mapperMethod對象,如果緩存中沒有,則創建一個,並添加到緩存中
final MapperMethod mapperMethod = cachedMapperMethod(method);
return mapperMethod.execute(sqlSession, args);
}
執行操作給了MapperMethod類,繼續追
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
switch (command.getType()) {
case INSERT: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.insert(command.getName(), param));
break;
}
case UPDATE: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.update(command.getName(), param));
break;
}
case DELETE: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.delete(command.getName(), param));
break;
}
case SELECT:
if (method.returnsVoid() && method.hasResultHandler()) {
executeWithResultHandler(sqlSession, args);
result = null;
} else if (method.returnsMany()) {
result = executeForMany(sqlSession, args);
} else if (method.returnsMap()) {
result = executeForMap(sqlSession, args);
} else if (method.returnsCursor()) {
result = executeForCursor(sqlSession, args);
} else {
// 處理返回爲單一對象的情況
// 通過參數解析器解析參數
Object param = method.convertArgsToSqlCommandParam(args);
result = sqlSession.selectOne(command.getName(), param);
}
break;
case FLUSH:
result = sqlSession.flushStatements();
break;
default:
throw new BindingException("Unknown execution method for: " + command.getName());
}
if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
throw new BindingException("Mapper method '" + command.getName()
+ " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
}
return result;
}
根據sql語句的不同類型,將執行權交給了Sqlsession
DefaultSqlSession交給Executor(執行器)
@Override
public int update(String statement, Object parameter) {
try {
dirty = true;
// statement爲命名空間+方法名
MappedStatement ms = configuration.getMappedStatement(statement);
return executor.update(ms, wrapCollection(parameter));
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error updating database. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
接着調用會依次經過CachingExecutor和SimpleExecutor
因爲我們發現DefaultSqlSession傳入的Executor通過如下方法返回的
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
executorType = executorType == null ? defaultExecutorType : executorType;
// 防止粗心大意的人將defaultExecutorType設爲null
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;
}
方法傳入的executorType爲defaultExecutorType
protected ExecutorType defaultExecutorType = ExecutorType.SIMPLE;
而cacheEnabled(控制二級緩存)默認值爲true,所用會用CachingExecutor裝飾SimpleExecutor
接着進入SimpleExecutor的doUpate方法
@Override
public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
Statement stmt = null;
try {
Configuration configuration = ms.getConfiguration();
StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
stmt = prepareStatement(handler, ms.getStatementLog());
return handler.update(stmt);
} finally {
closeStatement(stmt);
}
}
創建了一個PreparedStatementHandler,因爲從MappedStatement類中可以看到statementType類型爲PREPARED
mappedStatement.statementType = StatementType.PREPARED;
並且從PreparedStatementHandler得到一個PreparedStatement
handler.parameterize(stmt);
接着PreparedStatementHandler使用ParameterHandler設置SQL中的參數
到了SimpleStatementHandler的update方法
@Override
public int update(Statement statement) throws SQLException {
String sql = boundSql.getSql();
Object parameterObject = boundSql.getParameterObject();
KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();
int rows;
if (keyGenerator instanceof Jdbc3KeyGenerator) {
statement.execute(sql, Statement.RETURN_GENERATED_KEYS);
rows = statement.getUpdateCount();
keyGenerator.processAfter(executor, mappedStatement, statement, parameterObject);
} else if (keyGenerator instanceof SelectKeyGenerator) {
statement.execute(sql);
rows = statement.getUpdateCount();
keyGenerator.processAfter(executor, mappedStatement, statement, parameterObject);
} else {
statement.execute(sql);
rows = statement.getUpdateCount();
}
return rows;
}
終於看到JDBC的原生寫法
statement.execute()
至此,刪除流程完畢
整個執行的流程圖如下
Executor詳解
SimpleExecutor:默認配置,使用PreparedStatement對象訪問數據庫,每次訪問都要創建新的PreparedStatement對象
ReuseExecutor:使用PreparedStatement對象訪問數據庫,訪問時會重用statement對象
BatchExecutor:實現執行多條SQL語句的能力
總結
調用鏈路如下
MapperProxy:攔截Mapper方法
MapperMethod:根據語句的不同類型調用DefaultSqlSession
DefaultSqlSession:將執行權交給Executor
Executor:生成StatementHandler
ParameterHandler:將生成的StatementHandler中的SQL進行預編譯
ResultSetHandler:對數據庫返回的結果集(ResultSet)進行封裝,返回用戶指定的實體類型
參考博客
[1]https://www.jianshu.com/p/46c6e56d9774