MyBatis-MyBatis 插件

  1. 插件调用过程:
    MyBatis 插件使用的责任链模式, 这里的责任链模式是结合动态代理来实现的. 假设现在存在按顺序配置的 A, B, C 三个签名相同的拦截器, MyBatis 会按照 C>B>A>target.proceed()>A>B>C 顺序执行.

    /**
     * 接口
     */
    public interface HelloWorld {
        void sayHelloWorld();
    }
    
    /**
     * 接口实现
     */
    public class HelloWorldImpl implements HelloWorld {
        @Override
        public void sayHelloWorld() {
            System.out.println("Hello World")
        }
    }
    
    /**
     * 代理处理对象
     */
    public class InterceptorJdkProxy implements InvocationHandler {
        // 真实对象
        private Object target;
        // 拦截器全限定名
        private String interceptorClass;
        
        public IntegerceptorJdkProxy(Object target, String interceptorClass) {
            this.target = target;
            this.interceptorClass = interceptorClass;
        }
        
        public static Object bind(Object target, String interceptorClass) {
            return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InterceptorJdkProxy(target, interceptorClass));
        }
        
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            if (interceptorClass == null) {
                return method.invoke(target, args);
            }
            Object result = null;
            Interceptor interceptor = (Interceptor)Class.forName(interceptorClass).newInstance();
            if (interceptor.before(proxy, target, method, args)) { 
                result = method.invoke(target, args);
            } else {
                interceptor.around(proxy, target, method, args);
            }
            interceptor.after(proxy, target, method, args)l
            return result;
        }
    }
    
    /**
     * 拦截器接口
     */
    public interface Interceptor {
        public boolean before(Object proxy, Object target, Method method, Object[] args);
        
        public void around(Object proxy, Object target, Method method, Objects[] args);
        
        public void after(Object proxy, Object target, Method method, Objects[] args);
    }
    
    public class Interceptor1 implements Interceptor {
        public boolean before(Object proxy, Object target, Method method, Object[] args) {
            System.out.println("拦截器 1 的 before 方法");
            return true;
        }
        
        public void around(Object proxy, Object target, Method method, Objects[] args) {}
        
        public void after(Object proxy, Object target, Method method, Objects[] args) {
            System.out.println("拦截器 1 的 after 方法");
        }
    }
    
    public class Interceptor2 implements Interceptor {
        public boolean before(Object proxy, Object target, Method method, Object[] args) {
            System.out.println("拦截器 2 的 before 方法");
            return true;
        }
        
        public void around(Object proxy, Object target, Method method, Objects[] args) {}
        
        public void after(Object proxy, Object target, Method method, Objects[] args) {
            System.out.println("拦截器 2 的 after 方法");
        }
    }
    
    public class Interceptor3 implements Interceptor {
        public boolean before(Object proxy, Object target, Method method, Object[] args) {
            System.out.println("拦截器 3 的 before 方法");
            return true;
        }
        
        public void around(Object proxy, Object target, Method method, Objects[] args) {}
        
        public void after(Object proxy, Object target, Method method, Objects[] args) {
            System.out.println("拦截器 3 的 after 方法");
        }
    }
    
    public class Client {
        public static void main(String[] args) {
            // 这里的实际对象是 HelloImpl 实例
            HelloWorld proxy1 = (HelloWorld)InterceptorJdkProxy.bind(new HelloImpl(), "study.Interceptor1");
            // 注意这里实际对象是 proxy1
            HelloWorld proxy2 = (HelloWorld)InterceptorJdkProxy.bind(proxy1,
            "study.Interceptor2");
            // 注意这里实际对象是 proxy2
            HelloWorld proxy3 = (HelloWorld)InterceptorJdkProxy.bind(proxy2,
            "study.Interceptor3");
            proxy3.sayHelloWorld();
        }
    }
    

    调用 proxy3.sayHelloWorld() 方法会进入 InterceptorJdkProxy#invoke() 方法, 这时产生一个 Interceptor3 实例对象, 然后调用该实例对象的 before() 方法并返回 true, 然后调用 target 的 sayHelloWorld() 方法, 这时的 target 为 proxy2;

    调用 proxy2.sayHelloWorld() 方法又会进入 InterceptorJdkProxy#invoke() 方法, 这时产生一个 Interceptor2 实例对象, 然后调用该实例对象的 before() 方法并返回 true, 然后调用 target 的 sayHelloWorld() 方法, 这时的 target 为 proxy1;

    调用 proxy1.sayHelloWorld() 方法又会进入 InterceptorJdkProxy#invoke() 方法, 这时产生一个 Interceptor1 实例对象, 然后调用该实例对象的 before() 方法并返回 true, 然后调用 target 的 sayHelloWorld() 方法, 这时的 target 为 HelloWorldImpl 对象的实例.

    它的方法执行完毕后就会返回, 返回到 InterceptorJdkProxy#invoke() 方法中, 执行 Interceptor1 实例对象的 after 方法;

    Interceptor1 实例对象的 after 方法执行完毕后又返回到 InterceptorJdkProxy#invoke() 方法, 执行 Interceptor2 实例对象的 after 方法;
    Interceptor2 实例对象的 after 方法执行完毕后又返回到 InterceptorJdkProxy#invoke() 方法, 执行 Interceptor3 实例对象的 after 方法. 至此所有拦截器方法均被执行. 执行结果如下:

    拦截器 1 的 before 方法
    拦截器 2 的 before 方法
    拦截器 3 的 before 方法
    Hello World
    拦截器 3 的 after 方法
    拦截器 2 的 after 方法
    拦截器 1 的 after 方法
    

    整个调用过程呈链式调用结构.

  2. MyBatis 允许拦截的接口和方法

    • Executor(update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
    • ParameterHandler(getParameterObject, setParamters)
    • ResultSetHandler(handleResultSets, handleCursorResultSets, handleOutputParameters)
    • StatementHandler(prepare, parameterize, batch, update, query)
  3. MyBatss 拦截器接口:

    public interface Interceptor {
        Object intercept(Invocation invocation) throws Throwable;
        
        Object plugin(Object target);
        
        void setProperties(Properties properties);
    }
    
    • setProperties() 方法用来传递插件的参数, 该参数配置在 mybatis-config.xml 中:
    <plugin interceptor="study.MyInterceptor">
        <property name="prop1" value="value1"/>
        <property name="prop2" value="value2"/>
    </plugin>
    
    • plugin() 方法的 target 参数就是拦截器要拦截的对象, 该方法会在创建被拦截的接口实现类时被调用. 该方法的简单实现如下:
    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }
    
    • intercept() 方法, 通过该方法的参数 invocation 可以得到很多有用的信息:
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        Object target = invocation.getTarget();
        Method method = invocation.getMethod();
        Object[] args = invocation.getArgs();
        Object result = invocation.proceed();
        return result;
    }
    
  4. 拦截器签名:

    • @Intercepts 注解, 它的属性是一个 @Signature 数组.
    • @Signature 注解, 包含三个属性:
      • type: 设置拦截的接口
      • method: 设置拦截器接口中的方法名, 需要和接口匹配
      • args: 设置拦截方法的参数类型数组, 通过方法名和参数类型可以确定唯一一个方法
  5. 下画线键值转小写驼峰形式插件:

    @Intercepts(
        @Signatue(
            type=ResultSetHandler.class,
            method="handleResultSets",
            args={Statement.class}
        )
    )
    
    @SuppressWarnings({"unchecked", "rawtypes"})
    public class CameHumpInterceptor implements Interceptor {
        
        @Override
        public Object intercept(Invocation invocation) throws Throwable {
            // 这里拿到返回结果, 然后对返回结果进行处理
            List<Object> list  = (List<Object>)invocation.proceed();
            for (Object object : list) {
                if (object instanceof Map) {
                    processMap((Map)object);
                } else {
                    break;
                }
            }
            return list;
        }
        // 省略其它方法
    }
    
  6. 参考:
    [1] : Java EE互联网轻量级框架整合开发
    [2] : MyBatis从入门到精通

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值