Mybatis插件源码解读

一、责任链模式

Mybatis插件按照`责任链模式`实现。

[责任链,菜鸟教程](http://www.runoob.com/design-pattern/chain-of-responsibility-pattern.html)

责任é¾æ¨¡å¼ç UML å¾

最核心的每个Logger的logMessage()方法都可以选择自己执行或者传递给下一个执行,从而形成了一个链。

package cn.baopz.module;

/**
 * @author baopz
 */
public abstract class AbstractLogger {
    public static final String INFO = "info";
    public static final String DEBUG = "debug";
    public static final String ERROR = "error";

    private AbstractLogger next;

    public AbstractLogger getNext() {
        return next;
    }

    protected void setNext(AbstractLogger next) {
        this.next = next;
    }

    /**
     * 实现
     * @param level
     */
    public abstract void logger(String level);

}
package cn.baopz.module;

public class Debug extends AbstractLogger {
    @Override
    public void logger(String level) {
        if(level.equals(AbstractLogger.DEBUG)){
            System.out.println("debug 消息处理");
            new Exception("#").printStackTrace();
        }else{
            getNext().logger(level);
        }
    }
}
package cn.baopz.module;

public class Error extends AbstractLogger {
    @Override
    public void logger(String level) {
        if (level.equals(AbstractLogger.ERROR)){
            System.out.println("error 处理");
            new Exception("#").printStackTrace();
        }else{
            getNext().logger(level);
        }
    }
}
package cn.baopz.module;

public class Info extends AbstractLogger {
    @Override
    public void logger(String level) {
        if(level.equals(AbstractLogger.INFO)){
            System.out.println("info 处理");
            new Exception("#").printStackTrace();
        }else {
            getNext().logger(level);
        }
    }
}
package cn.baopz.module;

/**
 * @author baopz
 */
public class Client {
    public static void main(String[] args) {
        AbstractLogger debug = new Debug();
        AbstractLogger info = new Info();
        AbstractLogger error = new Error();

        debug.setNext(info);
        info.setNext(error);
        error.setNext(debug);

        info.logger(AbstractLogger.ERROR);
        info.logger(AbstractLogger.DEBUG);
    }
}

这里说明一下:new Exception("#").printStackTrace();这段代码可以打印栈运行轨迹,不错的调试手段。

结果如下:

error 处理
java.lang.Exception: #
debug 消息处理
	at cn.baopz.module.Error.logger(Error.java:8)
	at cn.baopz.module.Info.logger(Info.java:10)
	at cn.baopz.module.Client.main(Client.java:16)
java.lang.Exception: #
	at cn.baopz.module.Debug.logger(Debug.java:8)
	at cn.baopz.module.Error.logger(Error.java:10)
	at cn.baopz.module.Info.logger(Info.java:10)
	at cn.baopz.module.Client.main(Client.java:17)

二、Mybatis插件核心类

Mybatis中提供一个通用的拦截器,底层使用java的反射机制实现。

org.apache.ibatis.plugin.Interceptor接口,统一实现拦截的接口。

/**
 *    Copyright 2009-2015 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.apache.ibatis.plugin;

import java.util.Properties;

/**
 * 插件中需要用的拦截器接口
 * @author Clinton Begin
 */
public interface Interceptor {

  /**
   *
   *执行拦截逻辑的方法
   * @param invocation
   * @return
   * @throws Throwable
   */
  Object intercept(Invocation invocation) throws Throwable;

  /**
   * target被代理的对象
   * @param target
   * @return 返回增强的target
   */
  Object plugin(Object target);

    /**
     * 得到property name="someProperty" 的值,得到配置文件的值
     * <plugins>
     *   <plugin interceptor="org.mybatis.example.ExamplePlugin">
     *     <property name="someProperty" value="100"/>
     *   </plugin>
     * </plugins>
     * @param properties
     */
  void setProperties(Properties properties);

}

在实现该接口的类中添加两个注解org.apache.ibatis.plugin.Intercepts、org.apache.ibatis.plugin.Signature

/**
 *    Copyright 2009-2016 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.apache.ibatis.plugin;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @author Clinton Begin
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Intercepts {
  Signature[] value();
}

/**
 *    Copyright 2009-2016 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.apache.ibatis.plugin;

import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @author Clinton Begin
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({})
public @interface Signature {
  /**
   * 类名
   * @return
   */
  Class<?> type();

  /**
   * 方法
   * @return
   */
  String method();

  /**
   * 参数
   * @return
   */
  Class<?>[] args();
}

三、插件测试实现

具体实现如下,设置一个被拦截的接口。

package cn.baopz.plugin;

/**
 * 需要被拦截的接口
 * @author baopz
 */
public interface PluginInterface {

    /**
     * 测试被拦截的方法
     * @param test
     */
    void test(String test);

}

实现一个被拦截的实例

package cn.baopz.plugin;

/**
 * @author baopz
 */
public class Target implements PluginInterface {

    @Override
    public void test(String test) {
        System.out.println(test);
    }

}

一个拦截实例

package cn.baopz.plugin;

import org.apache.ibatis.plugin.*;

import java.util.Properties;

/**
 * @author baopz
 */
@Intercepts({@Signature(type = PluginInterface.class, method = "test", args = {String.class})})
public class InterceptorInstance implements Interceptor {
    private Properties properties;

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        Object[] objects = invocation.getArgs();
        for (Object object : objects) {
            System.out.println("被拦截的参数。" + object);
        }
        return invocation.proceed();
    }

    @Override
    public Object plugin(Object target) {
        System.out.println(target.getClass().getName());
        return Plugin.wrap(target, this);
    }

    @Override
    public void setProperties(Properties properties) {
        this.properties = properties;
    }
}

一个测试实例

package cn.baopz.plugin;

/**
 * @author baopz
 */
public class TestPlugin {

    public static void main(String[] args) {
        PluginInterface pluginInterface = new Target();
        pluginInterface = (PluginInterface) new InterceptorInstance().plugin(pluginInterface);
        pluginInterface.test("hello world");
    }
}

运行结果

cn.baopz.plugin.Target
被拦截的参数。hello world
hello world

测试实例分析:

需要被拦截的实例(PluginInterface pluginInterface = new Target();),通过实现的拦截器实例拦截,反射,实现自己的功能(pluginInterface = (PluginInterface) new InterceptorInstance().plugin(pluginInterface);),得到一个增强的目标实例,在目标实例执行的时候,执行了我们写的业务逻辑。

注意事项:通过后文的Plugin类,可以得知,如果拦截的是类本身,而非接口,那么拦截将会失败,继而直接执行的是目标类未被增强的方法。

四、目标争强核心类

org.apache.ibatis.plugin.Plugin、org.apache.ibatis.plugin.Invocation

/**
 * Copyright 2009-2017 the original author or authors.
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.ibatis.plugin;

import org.apache.ibatis.reflection.ExceptionUtil;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
 * plugin工具类型,反射工具
 * @author Clinton Begin
 */
public class Plugin implements InvocationHandler {

    private final Object target;
    private final Interceptor interceptor;
    private final Map<Class<?>, Set<Method>> signatureMap;

    private Plugin(Object target, Interceptor interceptor, Map<Class<?>, Set<Method>> signatureMap) {
        this.target = target;
        this.interceptor = interceptor;
        this.signatureMap = signatureMap;
    }

    /**
     * @param target 被代理的对象
     * @param interceptor 拦截器,主要用来执行org.apache.ibatis.plugin.Interceptor#intercept(org.apache.ibatis.plugin.Invocation)
     * @return
     */
    public static Object wrap(Object target, Interceptor interceptor) {
        //得到interceptor的签名
        Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);
        //被代理的类
        Class<?> type = target.getClass();
        //得到所有被注解的接口
        Class<?>[] interfaces = getAllInterfaces(type, signatureMap);
        if (interfaces.length > 0) {
            return Proxy.newProxyInstance(
                    type.getClassLoader(),
                    interfaces,
                    new Plugin(target, interceptor, signatureMap));
        }
        return target;
    }

    /**
     * 具体的包装类执行方法
     * @param proxy
     * @param method
     * @param args
     * @return object 执行的结构
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        try {
            //得到目标类所包含的所有方法,method.getDeclaringClass()得到目标类
            Set<Method> methods = signatureMap.get(method.getDeclaringClass());
            //找到注解的方法
            if (methods != null && methods.contains(method)) {
                //拦截方法,并执行注入到interceptor中的方法
                return interceptor.intercept(new Invocation(target, method, args));
            }
            return method.invoke(target, args);
        } catch (Exception e) {
            throw ExceptionUtil.unwrapThrowable(e);
        }
    }

    /**
     * 按照类(就是接口,接口在type中也是class的一种,如Map接口是Map.class类),记录下所有的方法
     * @param interceptor
     * @return
     */
    private static Map<Class<?>, Set<Method>> getSignatureMap(Interceptor interceptor) {
        //得到所有的注解
        Intercepts interceptsAnnotation = interceptor.getClass().getAnnotation(Intercepts.class);
        // issue #251
        if (interceptsAnnotation == null) {
            throw new PluginException("No @Intercepts annotation was found in interceptor " + interceptor.getClass().getName());
        }
        //得到注解的所有签名,可以拦截多个签名(类名,方法名,参数)
        Signature[] sigs = interceptsAnnotation.value();
        Map<Class<?>, Set<Method>> signatureMap = new HashMap<Class<?>, Set<Method>>();
        //所有的签名
        for (Signature sig : sigs) {
            //sig.type(),类(接口),多个方法
            Set<Method> methods = signatureMap.computeIfAbsent(sig.type(), k -> new HashSet<>());
            try {
                //得到方法
                Method method = sig.type().getMethod(sig.method(), sig.args());
                methods.add(method);
            } catch (NoSuchMethodException e) {
                throw new PluginException("Could not find method on " + sig.type() + " named " + sig.method() + ". Cause: " + e, e);
            }
        }
        return signatureMap;
    }

    /**
     * 获取目标类型实现的接口
     * @param type
     * @param signatureMap
     * @return
     */
    private static Class<?>[] getAllInterfaces(Class<?> type, Map<Class<?>, Set<Method>> signatureMap) {
        //接口容器
        Set<Class<?>> interfaces = new HashSet<Class<?>>();
        //循环把目标类,及其父类的所有被注解标记的接口记录下来
        while (type != null) {
            //所有接口,
            for (Class<?> c : type.getInterfaces()) {
                //目标类与注解的类一一对应
                if (signatureMap.containsKey(c)) {
                    interfaces.add(c);
                }
            }
            //父类
            type = type.getSuperclass();
        }
        return interfaces.toArray(new Class<?>[interfaces.size()]);
    }

}
/**
 * Copyright 2009-2017 the original author or authors.
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.ibatis.plugin;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * 调用
 * @author Clinton Begin
 */
public class Invocation {

    private final Object target;
    private final Method method;
    private final Object[] args;

    public Invocation(Object target, Method method, Object[] args) {
        this.target = target;
        this.method = method;
        this.args = args;
    }

    public Object getTarget() {
        return target;
    }

    public Method getMethod() {
        return method;
    }

    public Object[] getArgs() {
        return args;
    }

    public Object proceed() throws InvocationTargetException, IllegalAccessException {
        return method.invoke(target, args);
    }

}

核心代码分析:

核心代码

/**
     * @param target 被代理的对象
     * @param interceptor 拦截器,主要用来执行org.apache.ibatis.plugin.Interceptor#intercept(org.apache.ibatis.plugin.Invocation)
     * @return
     */
    public static Object wrap(Object target, Interceptor interceptor) {
        //得到interceptor的签名
        Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);
        //被代理的类
        Class<?> type = target.getClass();
        //得到所有被注解的接口
        Class<?>[] interfaces = getAllInterfaces(type, signatureMap);
        if (interfaces.length > 0) {
            return Proxy.newProxyInstance(
                    type.getClassLoader(),
                    interfaces,
                    new Plugin(target, interceptor, signatureMap));
        }
        return target;
    }

查找目标类及其父类的所有接口,通过反射方法(java.lang.reflect.Proxy#newProxyInstance),new出增强类。

五、实现在目标方法上链式添加所有拦截器

/**
 * Copyright 2009-2015 the original author or authors.
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.ibatis.plugin;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * 拦截器链
 * @author Clinton Begin
 */
public class InterceptorChain {

    /**
     * 存放插件的容器
     */
    private final List<Interceptor> interceptors = new ArrayList<Interceptor>();

    /**
     * 添加拦截器
     * @param target
     * @return
     */
    public Object pluginAll(Object target) {
        /**
         * 被所有拦截器增强的target
         */
        for (Interceptor interceptor : interceptors) {
            target = interceptor.plugin(target);
        }
        return target;
    }

    /**
     * 添加一个拦截器
     * @param interceptor
     */
    public void addInterceptor(Interceptor interceptor) {
        interceptors.add(interceptor);
    }

    /**
     * 得到所有的拦截器
     * @return
     */
    public List<Interceptor> getInterceptors() {
        return Collections.unmodifiableList(interceptors);
    }

}

六、MyBatis 允许使用插件来拦截的类型,方法

  • Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
  • ParameterHandler (getParameterObject, setParameters)
  • ResultSetHandler (handleResultSets, handleOutputParameters)
  • StatementHandler (prepare, parameterize, batch, update, query)

org.apache.ibatis.session.Configuration#interceptorChain

在mybatis统一配置类中(org.apache.ibatis.session.Configuration)提供了一个拦截器链(protected final InterceptorChain interceptorChain = new InterceptorChain()),拦截器链在四个方法中

org.apache.ibatis.session.Configuration#newParameterHandler

org.apache.ibatis.session.Configuration#newResultSetHandler

org.apache.ibatis.session.Configuration#newStatementHandler

org.apache.ibatis.session.Configuration#newExecutor(org.apache.ibatis.transaction.Transaction, org.apache.ibatis.session.ExecutorType)

均调用pluginAll方法添加到插件到链中。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值