Mybatis给我们提供了一种插件(plugin)的功能,虽然叫插件,但确实现这拦截器的功能。拦截器主要拦截Executor、ParameterHandler、ResultSetHandler、StatementHandler等几个方法。
拦截器的作用可以概括为:1.拦截执行器的方法2.拦截参数的处理3.拦截结果集的处理4.拦截Sql语句构建的处理


现在从源码来分析一下拦截器的实现方式,在XMLConfigBuilder中解析配置文件的pluginElement方法:
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).newInstance();interceptorInstance.setProperties(properties);configuration.addInterceptor(interceptorInstance);}}}
解析部分的代码就不贴出来了,主要是通过反射来实例化plngin节点中interceptor的类。然后调用全局配置类的Configuration的addInterceptor方法。下面我们来解析一下拦截器为何会拦截这些方法:
public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql);parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);return parameterHandler;}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;}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;}public Executor newExecutor(Transaction transaction, ExecutorType executorType, boolean autoCommit) {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, autoCommit);}executor = (Executor) interceptorChain.pluginAll(executor);return executor;}
我们可以看到是调用pluginAll方法来遍历所有的拦截器,然后调用每个拦截器的plugin方法来通过调用动态代理,生成代理对象并赋值给原先的对象。
Mybatis还提供了@Intercepts和@Signature注解和Plugin类的使用,下面我们来逐一分析下,就先从Plugin类的wrap方法下手:
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;}
我们可以看到这个方法返回了JDK提供的动态代理类,并且Plugin类实现了InvocationHandler接口。现在我们来wrap方法中的getSignatureMap方法:
private static Map<Class<?>, Set<Method>> getSignatureMap(Interceptor interceptor) {Intercepts interceptsAnnotation = interceptor.getClass().getAnnotation(Intercepts.class);if (interceptsAnnotation == null) { // issue #251throw 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;}
这个方法会拿到拦截器的@Intercepts注解,然后拿到这个注解的属性@Signature集合。遍历这个集合并拿出type属性(Class类型),根据这个type获取带有method属性和args属性的Method。最终返回一个type为key,value为Set<Method>的Map。下面来分析下wrap方法中的getAllInterfaces方法:
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()]);}
这个方法的作用是根据代理目标实例target返回signatureMap中含有target实现的接口数组。
Plugin类的作用是根据@Interceptors注解来得到这个注解属性的@Signature注解数组。最终根据target返回signatureMap中是否含有target实现的接口来决定是否返回一个代理对象来替换原先的target对象。
当返回代理对象之后,便执行Interceptor接口的interceptor方法。
在Mybatis的Interceptor拦截器接口中的三个方法,plugin方法用来某些处理器(Handler)的构建过程,interceptor用于处理代理类的执行,setProperties用来设置拦截器属性。




