Mybatis插件原理
Posted tripl
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Mybatis插件原理相关的知识,希望对你有一定的参考价值。
1、概述
Mybatis允许使用插件拦截的方法调用包括:
- Excetor
- ParameterHandler
- ResultHandler
- StatementHandler
上图是Mybatis框架的整体执行架构,Mybatis插件能够对四大对象接口进行拦截。
- Executor : Mybatis执行器,是Mybatis调度核心,负责SQL语句的完成和查询缓存的维护
- StatementHandler : 封装了JDBC Statement操作,负责对JDBC Statement的操作,如设置参数、将Statement结果集转换成list集合
- ParameterHandler :负责将用户传递的参数转换成JDBC Statement所需要的参数
- ResultSetHandler :负责将Java数据类型和JDBC数据类型之间的映射和转换
2、插件实现原理
Executor代理对象
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
executorType = executorType == null ? defaultExecutorType : executorType;
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;
}
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; }
可拦截类对象都是通过pluginAll来实现的
public Object pluginAll(Object target) { for (Interceptor interceptor : interceptors) { target = interceptor.plugin(target); } return target; }
default Object plugin(Object target) {
return Plugin.wrap(target, this);
}
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;
}
1、遍历所有拦截器,调用拦截器的plugin方法生成代理对象
2、注意:生成的对象又重新赋值给了target,所以如果有多个拦截器,生成的代理对象会被另一个代理对象所代理,形成一个代理链条,执行的时候,依次执行所有拦截器的拦截逻辑代码。
3、Proxy.newProxyInstance(type.getClassLoader(),interfaces,new Plugin(target, interceptor,signatureMap)) 动态代理实现
3、拦截逻辑的执行
由于真正去执行Executor、ParameterHandler、ResultSetHandler和StatementHandler类中的方法的对象是代理对象,所以在执行方法时,首先调用的时Plugin类,如下:
@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { try {
//首先根据执行方法所属类获取拦截器中声明需要拦截的方法集合 Set<Method> methods = signatureMap.get(method.getDeclaringClass());
//判断当前方法需不需要执行拦截逻辑,需要的话,执行拦截逻辑方法(即Interceptor接口的intercept方法实现 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); } }
以上是关于Mybatis插件原理的主要内容,如果未能解决你的问题,请参考以下文章