mybatis 提供了一种插件机制,其实就是一个拦截器,用于拦截 mybatis。
使用拦截器可以实现很多功能,比如: 记录执行错误的sql信息
本章简单讲解下 plugin 的运行机制
简单使用
添加一个拦截器非常简单:
- 实现 Interceptor 接口,
- 将拦截器注册到配置文件的
<plugins>
即可
该样例拦截 Executor 接口的 update 方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| @Intercepts({@Signature( type= Executor.class, method = "update", args = {MappedStatement.class,Object.class})}) public class ExamplePlugin implements Interceptor { public Object intercept(Invocation invocation) throws Throwable { return invocation.proceed(); } public Object plugin(Object target) { return Plugin.wrap(target, this); } public void setProperties(Properties properties) { } }
|
1 2 3
| <plugins> <plugin interceptor="org.format.mybatis.cache.interceptor.ExamplePlugin"></plugin> </plugins>
|
四大对象
Mybatis 中 有四类对象 Executor,ParameterHandler,ResultSetHandler,StatementHandler。
- Executor : DefaultSqlSession 是通过 Executor 完成查询的,而 Executor 是依赖 StatementHandler 完成与数据库的交互的。
- StatementHandler : 与数据库对话,会使用 parameterHandler 和 ResultHandler 对象为我们绑定SQL参数和组装最后的结果返回
- ParameterHandler : ParameterHandler 用于处理 sql 的参数,实际作用相当于对sql中所有的参数都执行 preparedStatement.setXXX(value);
- ResultSetHandler : 处理Statement执行后产生的结果集,生成结果列表
插件需要的相关注解
Intercepts
Intercepts 注解 表示该类为mybatis拦截器。该注解只有一个属性
Signature[] value();
表示 拦截其需要拦截的 类 和 方法。
Signature
Signature 注解。通过该注解可以确定拦截 具体哪一个类的哪个方法。
1 2 3 4 5 6 7 8 9 10 11 12 13
| @Documented @Retention(RetentionPolicy.RUNTIME) @Target({}) public @interface Signature { Class<?> type(); String method(); Class<?>[] args(); }
|
源码分析
拦截器接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
|
public interface Interceptor {
Object intercept(Invocation invocation) throws Throwable;
Object plugin(Object target);
void setProperties(Properties properties);
}
|
Plugin类
为了方便生成代理对象和绑定方法,MyBATIS为我们提供了一个 Plugin 类。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102
| package org.apache.ibatis.plugin;
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set;
import org.apache.ibatis.reflection.ExceptionUtil;
public class Plugin implements InvocationHandler {
private Object target; private Interceptor interceptor; private Map<Class<?>, Set<Method>> signatureMap;
private Plugin(Object target, Interceptor interceptor, Map<Class<?>, Set<Method>> signatureMap) { this.target = target; this.interceptor = interceptor; this.signatureMap = signatureMap; }
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()); 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); } }
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<Class<?>, Set<Method>>(); for (Signature sig : sigs) { Set<Method> methods = signatureMap.get(sig.type()); if (methods == null) { methods = new HashSet<Method>(); signatureMap.put(sig.type(), methods); } 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; }
private static Class<?>[] getAllInterfaces(Class<?> type, Map<Class<?>, Set<Method>> signatureMap) { Set<Class<?>> interfaces = new HashSet<Class<?>>(); while (type != null) { for (Class<?> c : type.getInterfaces()) { if (signatureMap.containsKey(c)) { interfaces.add(c); } } type = type.getSuperclass(); } return interfaces.toArray(new Class<?>[interfaces.size()]); }
}
|
小结
Plugin 和 Interceptor 的合作流程:
- 当外界需要新建一个对象时: 会调用 interceptor.plugin(Object target), 该方法会返回一个 target 的代理对象(简称为 proxy )。
- 当外界需要执行 target 的方法的时候,会委托 proxy 执行对应的方法。
- proxy 执行时,会根据 拦截器上的注解内容 判断是否进行拦截操作。若是,则调用 interceptor 的 intercept() 方法。
- intercept() 方法 必须执行 proxy 的 invoke 方法。
来源
https://www.cnblogs.com/fangjian0423/p/mybatis-interceptor.html
https://blog.csdn.net/qq924862077/article/details/53197778
https://blog.csdn.net/ykzhen2015/article/details/50349540