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);    }
0 0
原创粉丝点击