mybatis Interceptor使用和原理分析
我们在使用mybatis的过程中可能遇到一些通用的需求比如分页等,我们需要统一拦截一些方法,然后完成操作.mybatis为我们提供了Interceptor接口做这件事.但是mybatis的Interceptor只能拦截mybatis中指定的类和方法,并不能拦截所有的方法.
使用
我们只需要写一个类,然后继承mybatis的Interceptor方法,然后使用@Intercepts注解说明需要拦截的类和方法即可.可以在@Intercepts中配置多个类和方法,用于拦截多个方法.然后把我们定义的类定义到mybatis的配置文件的<plugins><plugin>元素中.
mybatis在解析配置文件的时候会自动的为我们生成代理,拦截我们需要拦截的方法.
mybatis中只能拦截下面类中的方法,其他的都不会进行拦截
- Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
- ParameterHandler (getParameterObject, setParameters)
- ResultSetHandler (handleResultSets, handleOutputParameters)
- StatementHandler (prepare, parameterize, batch, update, query)
//使用@Intercepts定义需要拦截的类,和方法,并且指明方法的入参(因为再mybatis中有很多重载的方法) @Intercepts({@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})}) //继承Interceptor接口,并且实现intercept方法. public class PagePlugin implements Interceptor { /** * intercept方法会有一个Invocation参数,里面封装了原始的被调用的方法名称和方法参数, * 我们可以直接使用Invocation进行获取,如果需要继续执行则直接调用Invocation的proceed方法即可 */ @Override public Object intercept(Invocation iv) throws Throwable { Method method = iv.getMethod(); Object[] args = iv.getArgs(); return iv.proceed(); } }
原理
我们知道了怎么使用mybatis的interceptor下面说一下mybatis的interceptor是怎么生效的.
1.读取,
我们在mybatis配置文件的整体加载过程一文中提到,在XmlConfigBuilder.parseConfiguration方法中会解析配置文件中的plugins元素.读取其所有的子元素,然后根据interceptor属性找到真正的拦截器,并调用默认的构造方法对其实例化,然后调用Interceptor.setProperties方法设置属性,并且全部保存到InterceptorChain中.
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); } } }
2.代理
因为mybatis中的Interceptor只会拦截Executor,ParameterHandler,ResultHandler,StatementHandler四个类,所以mybatis只需要在创建这四个类的时候生成代理就行了,而这四个类的生成都是mybatis通过Configuration生成的,所以mybatis只需要在创建这四个类的地方生成代理就可以了.
这里以Executor为例子说明一下mybatis生成代理的过程.Configuration.newExecutor方法生成Executor的时候会调用interceptorChain的pluginAll方法,对新创建的对象生成代理.
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; }
在pluginAll方法中,会遍历mybatis已经加载的所有的Interceptor,然后调用Interceptor.plugin方法生成代理.
public Object pluginAll(Object target) { for (Interceptor interceptor : interceptors) { target = interceptor.plugin(target); } return target; }
在Interceptor.plugin中只有一行代码,就是调用了Plugin.wrap(object,Interceptor),在wrap方法中,mybatis创建了plugin对象,而plugin类继承了InvocationHandler接口.在plugin中的invoke方法中,根据@interceptor注解中的配置,选择之际额调用targer的方法还是调用interceptor的intercept方法.
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); } }
posted on 2022-08-25 22:03 monkeydai 阅读(1920) 评论(0) 编辑 收藏 举报