Interceptor
public interface Interceptor {
Object intercept(Invocation invocation) throws Throwable;
default Object plugin(Object target) {
return Plugin.wrap(target, this);
}
default void setProperties(Properties properties) {
// NOP
}
}
简单介绍一下Interceptor 这个接口,里面有三个方法,作用分别是
intercept : 这个方法没有加default ,显然是mybatis 希望我们去实现的方法,这个方法 的参数 Invocation,通过这个对象能反射带哦用原
来对象的方法
plugin : target 是被拦截对象 ,可以看到,注意,这个方法其实mybatis 提供了默认的实现 ,而再 Plugin.wrap(target, this)里面的逻辑其
实非常的简单,其实就是为为拦截的对象生成了一个代理对象,为什么要生成代理对象呢? 其实也是非常容易理解,我么编写插件的
目的其实就是为了去增强方法,那么动态代理无疑是非常好的选择
setProperties:见名知意,设置属性,就是允许再这个方法里面进行属性的设置,方法再插件初始化的时候被调用
,如下
private void pluginElement(XNode parent) throws Exception {
if (parent != null) {
for (XNode child : parent.getChildren()) {
String interceptor = child.getStringAttribute("interceptor");
Properties properties = child.getChildrenAsProperties();
Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).getDeclaredConstructor().newInstance();
interceptorInstance.setProperties(properties); //!!!!!!!!!!!!!!!!!!!!!
configuration.addInterceptor(interceptorInstance);
}
}
}
原理
如果以拦截Executor,那么就得就得从获取 SqlSession 说起
获取SqlSession 里面很关键的一步就是创建执行器 ------------Configruation # newExecutor
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
//代码省略
executor = new SimpleExecutor(this, transaction);
if (cacheEnabled) {
executor = new CachingExecutor(executor);
}
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
默认情况下是创建一个叫做SimpleExecutor 的执行器,那么创建完执行器后,就执行了一个非常眼熟的方法 pluginAll(executor),这个方法似乎跟 Interceptor 里面的 plugin 方法非常的像,
public Object pluginAll(Object target) {
for (Interceptor interceptor : interceptors) {
target = interceptor.plugin(target);
}
return target;
}
interceptors:是一个list ,存储着开发人员配置的Interceptor
没错,这里面会是进行 plugin方法的回调!!!
而plugin 前面说过,里面 mybatis 提供了默认的实现,就是创建代理对象,并且这里是再for循环里面进行代理,也就是说将代理对象返回出来,又进行了一层代理,一层一层又一层…
不论怎么代理,那么再执行真正的数据库请求的执行一定是会执行相对应的invoke方法
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));
}
return method.invoke(target, args);
} catch (Exception e) {
throw ExceptionUtil.unwrapThrowable(e);
}
}
首先会根据签名过滤出需要拦截的方法,如果需要拦截,那么就会执行 Interceptor 的intercept()方法,完成拦截的逻辑
以上是完成Executor 的拦截,除此,mybatis 还支持 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);
statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
return statementHandler;
}
ParameterHandler 的拦截
public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql);
parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);
return parameterHandler;
}
ResultSetHandler 的拦截
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);
resultSetHandler = (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler);
return resultSetHandler;
}
这三个其实与Executor大同小异,实现原理类似