本次我們學習mybatis的插件,重點是理解他和動態代理的關係
動態代理簡單回顧
上節我們已經詳細的瞭解動態代理;主要是通過定義接口,通過
newProxyInstance(ClassLoader loader,Class<?>[] interfaces,
InvocationHandler h)
方法,創建一個接口的代理。interfaces 表示代理也需要遵守的協議,還有h是實現
InvocationHandler協議的實例,其中有一個方法invoke,也是邏輯增強或者類功能增強的地方。具體請參考:使用JDK API實現動態代理和源碼分析
下面我們正式分析mybatis的插件
Mybatis 插件原理分析
@Intercepts(@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}))
public class TenantInterceptor implements Interceptor {
我們最常見的插件應該就是這樣的,首先實現Interceptor方法接口;
Object intercept(Invocation invocation) throws Throwable;
default Object plugin(Object target) {
return Plugin.wrap(target, this);
}
我們關注上面上面這兩個方法。
首先我們給出答案,intercept方式是邏輯增強的方法,plugin是創建代理的方法。
但是直接看好像和我們動態代理沒有關係
生成代理
我們從 return Plugin.wrap(target, this);
方法開始,也就是Interceptor 的第二個方法,在wrap方法中創建了代理
public static Object wrap(Object target, Interceptor interceptor) { // 1
Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);
Class<?> type = target.getClass();
Class<?>[] interfaces = getAllInterfaces(type, signatureMap);
if (interfaces.length > 0) {
return Proxy.newProxyInstance( // 2
type.getClassLoader(),
interfaces,
new Plugin(target, interceptor, signatureMap));
}
return target;
}
- 方法的第一個參數object就是需要增強的對象,第二個就是Interceptor,因爲Interceptor的
intercept
有增強邏輯,例如修改SQL,分頁插件等等,是我們生成代理需要的。 - 正式的生成代理對象,其中用到了
Plugin
,Plugin
類一定實現InvocationHandler接口。
邏輯增強 InvocationHandler
還是在Plugin
中的invoke
方法,也就是InvocationHandler
接口方法。
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
Set<Method> methods = signatureMap.get(method.getDeclaringClass());
if (methods != null && methods.contains(method)) {
return interceptor.intercept(new Invocation(target, method, args));//1
}
return method.invoke(target, args);//2
} catch (Exception e) {
throw ExceptionUtil.unwrapThrowable(e);
}
}
- 我們實現的Interceptor實例的
interceptor
方法生效了,也就是邏輯增強 - 被代理類的方法執行
代理創建的觸發時機
在Mybatis中重要的接口Executor
和StatementHandler
,其中的方法逐層調用,都是在爲執行SQL配置環境,其中StatementHandler就是。
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
上面是通過configuration配置類,創建StatementHandler
,下面是具體的代碼。
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
StatementHandler c= new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql); // 1
statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
return statementHandler; // 2
}
- 創建StatementHandler
- 使用interceptorChain增強statementHandler
也就是每次我們通過SqlSession獲取Mapper,在Mapper上調用方法執行SQL時,都會觸發interceptorChain增強對象。
interceptorChain是在configuration中存儲的,就是插件集合
public Object pluginAll(Object target) {
for (Interceptor interceptor : interceptors) {
target = interceptor.plugin(target); // 1
}
return target;
}
pluginAll
方法,具體的增加就是創建調用我們編寫插件的plugin方法,創建代理對象,例如
public Object plugin(Object target) {
return Plugin.wrap(target,this); // 創建實例
}
總結
插件就是在調用Mapper方法時,通過動態代理對Mybatis的某些接口增強,通過Interceptor的 @Signature註解指定需要增強的接口方法.
還有很多細節,歡迎大家討論學習