MyBatis在四大对象的创建过程中,都会有插件进行介入。
interceptorChain.pluginAll
public Object pluginAll(Object target) { for (Interceptor interceptor : interceptors) { target = interceptor.plugin(target); } return target; }
插件原理
在四大对象创建的时候
1. 每个创建出来的对象不是直接返回的,而是
interceptorChain.pluginAll(parameterHandler);
2. 获取到所有的Interceptor(拦截器)(插件需要实现的接口);
调用interceptor.plugin(target);返回target包装后的对象
3. 插件机制,我们可以使用插件为目标对象创建一个代理对象;AOP(面向切面)
我们的插件可以为四大对象创建出代理对象;
代理对象就可以拦截到四大对象的每一个执行;
插件编写
1、编写Interceptor的实现类
2、使用@Intercepts注解完成插件签名
3、将写好的插件注册到全局配置文件中
源码
如我们写的MyFirstPlugin,其插件签名为:
@Intercepts({@Signature(type = StatementHandler.class,method = "parameterize",args = Statement.class)})
那么将其纳入到我们mybatis查询的逻辑里面,是在BaseStatementHandler构造方法执行的时候,执行
this.parameterHandler = configuration.newParameterHandler(mappedStatement, parameterObject, boundSql);
在newParameterHandler时
parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);
调用到MyFirstPlugin的plugin方法
Object wrap = Plugin.wrap(o, this);
在wrap方法中,会进行判断是不是指定的type,如果是指定的,那么就创建一个代理对象
public static Object wrap(Object target, Interceptor interceptor) { if (interfaces.length > 0) { return Proxy.newProxyInstance( type.getClassLoader(), interfaces, new Plugin(target, interceptor, signatureMap)); } return target; }
注意:return Proxy.newProxyInstance(new Plugin)
之后返回到SimpleExecutor中执行prepareStatements方法
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException { Statement stmt; Connection connection = getConnection(statementLog); stmt = handler.prepare(connection, transaction.getTimeout()); handler.parameterize(stmt); return stmt; }
这个handler就是我们前面创建的代理对象。
在prepare和parameterize方法中,如果其为代理对象,那么会执行Plugin class的invoke方法
@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)); } return method.invoke(target, args); } catch (Exception e) { throw ExceptionUtil.unwrapThrowable(e); } }
如果method对应上了,那么就执行interceptor.intercept,去执行MyFirstPlugin的intercept接口
没有对应上,那么就直接放行
多个插件运行流程
当我定义两个相同的plugin的时候,
多个插件会产生多层代理的一个代理对象
原因就是:
public Object pluginAll(Object target) { for (Interceptor interceptor : interceptors) { target = interceptor.plugin(target); } return target; }
target = interceptor.plugin(target);这句,由MyFirstPlugin包装好的代理对象再作为target,再生成一次target
调用Plugin的invoke方法的时候
@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)); } return method.invoke(target, args); } catch (Exception e) { throw ExceptionUtil.unwrapThrowable(e); } }
就会先调用MySecondFilter的intercept方法,再调用MyFirstFilter的intercept方法。