【第22期】观点:IT 行业加班,到底有没有价值?

MyBatis拦截器

原创 2016年09月04日 11:18:24

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中的插件原理和应用

如果你不懂反射和动态代理请参考我的博文:http://blog.csdn.net/ykzhen2015/article/details/50312651 这是本文的基础,请先掌握它,否则下面内容的将...

深入浅出Mybatis-插件原理

Mybatis采用责任链模式,通过动态代理组织多个拦截器(插件),通过这些拦截器可以改变Mybatis的默认行为(诸如SQL重写之类的),由于插件会深入到Mybatis的核心,因此在编写自己的插件前最...

程序员升职加薪指南!还缺一个“证”!

CSDN出品,立即查看!

mybatis拦截器

原文地址(http://www.tuicool.com/articles/7bYjUn) 1.1 目录 1.1 目录 1.2 前言 1.3 Interceptor接口 1.4 注册拦截...

Mybatis插件原理分析(一)

我们首先介绍一下Mybatis插件相关的几个类,并对源码进行了简单的分析。 Mybatis插件相关的接口或类有:Intercept、InterceptChain、Plugin和Invocation,这...

读mybatis源码之十三:拦截器

一、拦截器配置 在configuration里面可以配置plugin,他是在XMLConfigBuilder装配进去的

Mybatis最入门---分页查询(拦截器分页原理及实现)

[一步是咫尺,一步即天涯] 前文,我们演示了物理分页的Sql实现方式,这种方式使得我们每次在编写查询服务时,不断的重复造轮子。这样的代码实现方式就显得十分的笨拙了。本文是Mybatis分页查询的最后一...

MyBatis插件原理-源码解读

MyBatis支持配置多个插件动态添加新的功能,因为存在InterceptorChain,很多人认为Mybatis采用责任链模式,看了源码后我觉得更像是装饰器模式。 Mybatis支持对Execut...

MyBATIS原理第三篇: SqlSession下的四大对象之一——执行器(executor)

首先我先解释一下标题 四大对象是指:executor, statementHandler,parameterHandler,resultHandler对象。(为了方便下面的文章说道四大对象就专指它们)...

Mybatis插件原理分析(二)

在上一篇中Mybatis插件原理分析(一)中我们主要介绍了一下Mybatis插件相关的几个类的源码,并对源码进行了一些解释,接下来我们通过一个简单的插件实现来对Mybatis插件的运行流程进行分析。 ...

在mybatis执行SQL语句之前进行拦击处理

比较适用于在分页时候进行拦截。对分页的SQL语句通过封装处理,处理成不同的分页sql。 实用性比较强。 import java.sql.Connection; import java.sql.Pre...
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)