MyBatis拦截器
来源:互联网 发布:交大网络教育学院网址 编辑:程序博客网 时间:2024/06/05 23:01
MyBatis拦截器
Mybatis拦截器分类
- Executor:执行拦截器,在执行SQL语句之前进行拦截
- ParameterHandler :参数设置拦截器,在设置SQL语句中的参数之前进行拦截
- ResultSetHandler :结果拦截器,在返回结果之前进行拦截
- StatementHandler :SQL语句构建拦截器,在构建SQL语句之前进行拦截
拦截器原理
拦截器最关键的原理是运用了责任链模式,总结一句话说就是,每次拦截都生成了一个当前类的代理类,多个拦截就进行了多次包装,在代理类上在生成代理类,这样就是先了多层拦截,下面我们以上面的Executor来举例说明是如何生成代理类的,其他的拦截器原理是一样的。
1.Executor的创建
在Configuration.class中对以上可以做拦截的四个对象进行了初始化,针对Executor的初始化代码如下
public Executor newExecutor(Transaction transaction, ExecutorType executorType) { executorType = executorType == null?this.defaultExecutorType:executorType; executorType = executorType == null?ExecutorType.SIMPLE:executorType; Object 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(this.cacheEnabled) { executor = new CachingExecutor((Executor)executor); } Executor executor1 = (Executor)this.interceptorChain.pluginAll(executor); return executor1; }
上述代码最关键的地方在于
Executor executor1 = (Executor)this.interceptorChain.pluginAll(executor);
通过字面意思可以看出他是调用了拦截器链来加载所有的拦截器,下面来看看具体的实现过程。
InterceptorChain.class
public Object pluginAll(Object target) { Interceptor interceptor; for(Iterator i$ = this.interceptors.iterator(); i$.hasNext(); target = interceptor.plugin(target)) { interceptor = (Interceptor)i$.next(); } return target; }
这里用了一个迭代器迭代interceptors,只要还存在interceptor就调用plugin(target)方法将target包装一次,至于怎么包装,这就是我们的interceptor自己来实现的咯
TestInterceptor.class
public Object plugin(Object o) { return Plugin.wrap(o,this); }
这里我调用了Plugin类的静态方法wrap并且传入了两个参数,一个是target(也就是executor),第二个是拦截器自生(也就是TestInterceptor对象)
最关见的步骤就是Plugin.wrap(o,this);
Plugin.class
public static Object wrap(Object target, Interceptor interceptor) { Map signatureMap = getSignatureMap(interceptor); Class type = target.getClass(); Class[] interfaces = getAllInterfaces(type, signatureMap); return interfaces.length > 0?Proxy.newProxyInstance(type.getClassLoader(), interfaces, new Plugin(target, interceptor, signatureMap)):target; }
上面的代码我们需要一行一行的来分析,首先是【signatureMap】这个map对象里面到底存了怎样的key-value,这需要分析 getSignatureMap(interceptor)方法做了什么,首先传入的参数是我们自定义的Interceptor对象(也就是TestInterceptor对象)
private static Map<Class<?>, Set<Method>> getSignatureMap(Interceptor interceptor) { Intercepts interceptsAnnotation = (Intercepts)interceptor.getClass().getAnnotation(Intercepts.class); if(interceptsAnnotation == null) { throw new PluginException("No @Intercepts annotation was found in interceptor " + interceptor.getClass().getName()); } else { Signature[] sigs = interceptsAnnotation.value(); HashMap signatureMap = new HashMap(); Signature[] arr$ = sigs; int len$ = sigs.length; for(int i$ = 0; i$ < len$; ++i$) { Signature sig = arr$[i$]; Object methods = (Set)signatureMap.get(sig.type()); if(methods == null) { methods = new HashSet(); signatureMap.put(sig.type(), methods); } try { Method e = sig.type().getMethod(sig.method(), sig.args()); ((Set)methods).add(e); } catch (NoSuchMethodException var10) { throw new PluginException("Could not find method on " + sig.type() + " named " + sig.method() + ". Cause: " + var10, var10); } } return signatureMap; } }
这个方法的主要作用是读取自定义interceptor中的注解
@Intercepts(value = {@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})})
将读取的值解析出来存入一个HashMap,key为【 Executor.class】,value为Executor类的update方法的Set集合,接着分析
Class type = target.getClass();
这个就是获取了传入的Executor这个接口的Class对象,接着
Class[] interfaces = getAllInterfaces(type, signatureMap);
private static Class<?>[] getAllInterfaces(Class<?> type, Map<Class<?>, Set<Method>> signatureMap) { HashSet interfaces; for(interfaces = new HashSet(); type != null; type = type.getSuperclass()) { Class[] arr$ = type.getInterfaces(); int len$ = arr$.length; for(int i$ = 0; i$ < len$; ++i$) { Class c = arr$[i$]; if(signatureMap.containsKey(c)) { interfaces.add(c); } } } return (Class[])interfaces.toArray(new Class[interfaces.size()]); }
这两步主要的作用是找到xxxExecutor实现了哪些接口,将这些接口中存在于signatureMap的组装成一个数组返回(因为有些接口是不需要做拦截的)。
剩下的事情就比较简单了。
return interfaces.length > 0?Proxy.newProxyInstance(type.getClassLoader(), interfaces, new Plugin(target, interceptor, signatureMap)):target;
查看是否有需要拦截的接口,如果没有就返回target(Executor)本生,否则返回生成的动态代理类来进行代理。接着进入下一个拦截循环。
举个例子,我们定义了两个拦截器,都是拦截Executor的,分别为InterceptorA和InterceptorB,那么最终生成的代理类是(Executor$InterceptorA)$InterceptorB,而代理类的InvocationHandler就是在Plugin.class,那么我们来看看他的invoke方法
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { try { Set e = (Set)this.signatureMap.get(method.getDeclaringClass()); return e != null && e.contains(method)?this.interceptor.intercept(new Invocation(this.target, method, args)):method.invoke(this.target, args); } catch (Exception var5) { throw ExceptionUtil.unwrapThrowable(var5); } }
这个方法比较简单,就是检查当前调用的方法是否在signatureMap中如果不在,则不进行拦截直接调用method.invoke(this.target, args),否则调用interceptor的intercept方法,传入的参数为Invocation里面包含了target(下一个代理类或者是源类),method,args。而在自己实现的interceptor中直接
TestInterceptor.java
public Object intercept(Invocation invocation) throws Throwable { System.out.println("====================================before====================================="); RoutingStatementHandler rsh = (RoutingStatementHandler)invocation.getTarget(); System.out.println(rsh.getBoundSql().getSql()); Class<?> classType = rsh.getClass(); Field field = classType.getDeclaredField("delegate"); field.setAccessible(true); BaseStatementHandler sh = (BaseStatementHandler)field.get(rsh); BoundSql bsql = sh.getBoundSql(); Class<?> classType2 = bsql.getClass(); Field field2 = classType2.getDeclaredField("sql"); field2.setAccessible(true); String sql = (String)field2.get(bsql); field2.set(bsql,sql + " where id = 1") ; return invocation.proceed(); }
直接调用return invocation.proceed();则可进入下一层拦截
Invocation.class
public Object proceed() throws InvocationTargetException, IllegalAccessException { return this.method.invoke(this.target, this.args); }
- 【myBatis】Mybatis中的拦截器
- Mybatis中的拦截器
- mybatis分页拦截器
- Mybatis拦截器介绍
- Mybatis - 分页拦截器
- mybatis拦截器
- Mybatis 拦截器介绍
- Mybatis 拦截器
- Mybatis拦截器分页
- MyBatis拦截器
- mybatis拦截器
- mybatis分页拦截器
- MyBatis拦截器分页
- Mybatis拦截器
- mybatis拦截器
- Mybatis分页拦截器
- Mybatis分页拦截器
- MyBatis拦截器Inteceptor
- Java面试题第(1)季
- Android 获取你手机中安装的应用的包名及启动页
- 如何实现使用QtQuick循环轮播图,并支持用户滑动切换
- 【转】经验分享:CSS浮动(float,clear)通俗讲解
- html+css小技巧大用处
- MyBatis拦截器
- 版本回退
- grok 官方文档
- iOS 25个性能优化/内存优化常用方法
- iOS-相机,相册,位置访问权限设置
- 给 Android 开发者的 RxJava 详解
- Axis1.4发布WebService
- Spark线性代数,绘图工具入门;scala, java下的Breeze线性代数以及数据绘图工具breeze-viz入门
- (三)spring与Hibernate的整合