美文网首页
Mybatis 插件开发

Mybatis 插件开发

作者: 茧铭 | 来源:发表于2019-07-22 23:14 被阅读0次

           之前跟踪mybatis执行源码的时候,结合一些网络资料。知道了这个执行过程中有四个非常重要的对象,这四个对象在Mybatis中都支持自定义插件进行拦截处理。

    • Executor 执行器(update| query| commit| rollback等方法)
    • ParameterHandler 参数处理器(setParameters等方法)
    • ResultSetHandler 结果集处理器(handleResultSets| handleOutputParameters等方法)
    • StatementHandler SQL语法构造器(prepare| parameterize| batch| update| query等方法)

    这个四个对象在创建的过程中都不是new对象然后就直接创建的,他们在创建之前,都有这么一句this.interceptorChain.pluginAll(target);。这个pluginAll的方法也是挺简单的,就是遍历所有的interceptor对象,用来包装传入的四大对象,并返回包装后的内容。

    联想到了这儿,就可以知道插件的重点就是这个interceptor 拦截器。当前的这个Interceptor在Mybatis是一个接口,插件的实现第一步就是要实现这个接口。下面的例子是对内容的一些理解注释。

    第一步就是写一个要实现了Interceptor接口的类,这个类的作用描述了如何包装四大对象,以及如何拦截内容。在 intercept() 方法中的invocation.proceed()的前后对数据进行更改,包括修改sql和参数等等操作都可以。

    package com.huangyu.orderman.config;
    
    import org.apache.ibatis.executor.statement.StatementHandler;
    import org.apache.ibatis.plugin.*;
    
    import java.sql.Statement;
    import java.util.Properties;
    
    
    /**
     * type拦截对象,即StatementHandler 
     * method 拦截对象的方法,即parameterize方法
     * args是要拦截的参数,为了避免重载造成的影响,应该写将要拦截的方法的对应全部参数 ,这里就只有一个Statement 
     */
    @Intercepts({
            @Signature(
                type= StatementHandler.class,method = "parameterize" ,args = Statement.class
            )
    })
    public class MybatisPluginInterceptor implements Interceptor {
    
        /**
         * 拦截目标对象的目标方法的执行
         */
        @Override
        public Object intercept(Invocation invocation) throws Throwable {
            // TODO 这里写入拦截处理的步骤
    
            /** 执行目标方法,即执行本来就要执行的执行sql语句。如果没有这一句,就相当于直接返回了,不再操作原本应该执行的逻辑了,会破坏mybatis的内部结构  */
            Object proceed = invocation.proceed();
            return proceed;
        }
    
        /**
         * 包装目标对象,为目标创建一个代理对象
         *      即:将传入的四大对象包装一下,返回Proxy的代理对象。
         */
        @Override
        public Object plugin(Object target) {
            /**  Mybatis提供的包装对象Plugin */
            Object wrap = Plugin.wrap(target, this);
    
            /** 方法解析。getAllinterfaces(type, signatureMap) 这个方法在当前插件拦截器的注解中是否要拦截这个对象;
             *  比如说拦截器顶部的注解中要拦截Executor,interfaces.length才会大于0。否则就会跳过对Executor的包装
             *  包装代理对象也是利用Proxy代理对象处理,因此拦截之后会执行这个Plugin的invoke()的方法
             * public static Object wrap(Object target, Interceptor interceptor) {
                    Map<Class<?>, Set<Method>> 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;
             }
             */
    
            return wrap;
        }
    
        /**
         * 获取插件注册传递的参数包装成Propereties并自动获取到
         */
        @Override
        public void setProperties(Properties properties) {
            System.out.println(properties);
        }
    }
    

    设计到具体对象的具体方法,应该怎么去拦截,就需要多读读其他插件的拦截机制,并且十分熟悉mybatis的执行流程,才能在对的地方去拦截方法修改参数。

    最后呢,我们为了让这个插件能被使用到,需要将插件注册到全局配置文件mybatis-config.xml 的<plugin>的标签中

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE configuration
            PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-config.dtd">
    <configuration>
        <plugins>
            <plugin interceptor="com.huangyu.orderman.config.MybatisPluginInterceptor">
                <property name="name" value="huangyu"/> <!-- 插件传递的参数 -->
            </plugin>
        </plugins>
    </configuration>
    

    结束之前,补充一下上面的Proxy的代理对象的内容。



    Plugin代理对象包装内容之后,将原本要执行逻辑的四大对象,包装在了Object target中。代理对象执行了intercept() 方法后,再通过代码重点的 invocation.proceed() 方法执行原有的逻辑。
    那如果有多个拦截器同时拦截这个对象的话,会多层级地去包装这个对象。那么可能出现的结构就是

     PluginProxy2{
         interceptor : interceptor 2
         target : PluginProxy1{
                interceptor : interceptor 1
                target : Executor
         }
    }
    

    这样就说明了,先被包装的拦截方法会后执行!因此在有一些处理关系的情况下,应该将越先执行的插件,在mybatis-config.xml中越后注册进去。

    相关文章

      网友评论

          本文标题:Mybatis 插件开发

          本文链接:https://www.haomeiwen.com/subject/syuglctx.html