package org.apache.ibatis.session;
public class Configuration {
public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
// ...
parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);
return parameterHandler;
}
public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, RowBounds rowBounds, ParameterHandler parameterHandler,
// ...
resultSetHandler = (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler);
return resultSetHandler;
}
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
// ...
statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
return statementHandler;
}
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
// ...
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
}
只有這四個方法調用了pluginAll(...)
方法, 所以支持的可攔截的類型:Executor、StatementHandler、ParameterHandler 和 ResultSetHandler
- org.apache.ibatis.executor.Executor: 攔截執行器方法
- 實現類: CachingExecutor
- org.apache.ibatis.executor.statement.StatementHandler: 攔截SQL語法構建處理
- 實現類: RoutingStatementHandler
- org.apache.ibatis.executor.parameter.ParameterHandler: 攔截參數處理
- 實現類: DefaultParameterHandler
- org.apache.ibatis.executor.resultset.ResultSetHandler: 攔截結果集處理
- 實現類: DefaultResultSetHandler
package org.apache.ibatis.plugin;
public class InterceptorChain {
private final List<Interceptor> interceptors = new ArrayList<>();
/**
* 判斷當前 target 是否設置了攔截器
* */
public Object pluginAll(Object target) {
for (Interceptor interceptor : interceptors) {
target = interceptor.plugin(target);
}
return target;
}
}
@Intercepts(...)
public class XXXInterceptor implements Interceptor {
// ...
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
// ...
}
參考: com.github.pagehelper.PageInterceptor
package org.apache.ibatis.plugin;
public class Plugin implements InvocationHandler {
// ...
/**
* 包裝 目標對象(target) 爲 代理攔截器對象(interceptor)
*
* @param target
* 目標對象, 實際對象
* @param interceptor
* 代理攔截器對象
*/
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;
}
/**
*
* */
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
Set<Method> methods = signatureMap.get(method.getDeclaringClass());
// 判斷當前執行方法是否是自定義攔截器中的方法, 即 @Intercepts(@Signature(...)) 中籤名的方法
if (methods != null && methods.contains(method)) {
// 執行自定義攔截器內容
return interceptor.intercept(new Invocation(target, method, args));
}
return method.invoke(target, args);
} catch (Exception e) {
throw ExceptionUtil.unwrapThrowable(e);
}
}
/**
* 獲取 自定義攔截器 插件需要攔截的方法, 即註解 @Intercepts(...) 中配置的信息
*/
private static Map<Class<?>, Set<Method>> getSignatureMap(Interceptor interceptor) {
Intercepts interceptsAnnotation = interceptor.getClass().getAnnotation(Intercepts.class);
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;
}
// ...
}
註冊攔截器
InterceptorChain類中成員變量interceptors的值從哪來
mybatis.config-location=classpath:/mybatis/config/mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<plugins>
<plugin interceptor="com.jaemon.oms.utils.PagePlugin">
<property name="dialect" value="mysql" />
<property name="pageSqlId" value="^(find).*(Page)$" />
<property name="pageParam" value="pageInfo" />
</plugin>
</plugins>
</configuration>
package org.apache.ibatis.builder.xml;
public class XMLConfigBuilder extends BaseBuilder {
// ...
private void parseConfiguration(XNode root) {
// ...
pluginElement(root.evalNode("plugins"));
// ...
}
private void pluginElement(XNode parent) throws Exception {
if (parent != null) {
for (XNode child : parent.getChildren()) {
// 獲取 plugin 標籤的 interceptor 屬性值
String interceptor = child.getStringAttribute("interceptor");
Properties properties = child.getChildrenAsProperties();
Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance();
interceptorInstance.setProperties(properties);
// 註冊攔截器
configuration.addInterceptor(interceptorInstance);
}
}
}
}