第九章-Mybatis源码解析-插件

先看看官网对插件的描述:


MyBatis 允许你在映射语句执行过程中的某一点进行拦截调用。默认情况下,MyBatis 允许使用插件来拦截的方法调用包括:

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

这些类中方法的细节可以通过查看每个方法的签名来发现,或者直接查看 MyBatis 发行包中的源代码。 如果你想做的不仅仅是监控方法的调用,那么你最好相当了解要重写的方法的行为。 因为在试图修改或重写已有方法的行为时,很可能会破坏 MyBatis 的核心模块。 这些都是更底层的类和方法,所以使用插件的时候要特别当心。

通过 MyBatis 提供的强大机制,使用插件是非常简单的,只需实现 Interceptor 接口,并指定想要拦截的方法签名即可。

// ExamplePlugin.java
@Intercepts({@Signature(
  type= Executor.class,
  method = "update",
  args = {MappedStatement.class,Object.class})})
public class ExamplePlugin implements Interceptor {
  private Properties properties = new Properties();
  public Object intercept(Invocation invocation) throws Throwable {
    // implement pre processing if need
    Object returnObject = invocation.proceed();
    // implement post processing if need
    return returnObject;
  }
  public void setProperties(Properties properties) {
    this.properties = properties;
  }
}
<!-- mybatis-config.xml -->
<plugins>
  <plugin interceptor="org.mybatis.example.ExamplePlugin">
    <property name="someProperty" value="100"/>
  </plugin>
</plugins>

上面的插件将会拦截在 Executor 实例中所有的 “update” 方法调用, 这里的 Executor 是负责执行底层映射语句的内部对象。


由此,可以得出,Mybatis支持四种类型的插件,并且可以通过实现 Interceptor 接口,完成自定义的插件功能,最后再通过标签配置,加载到Mybatis应用中。那接下来,就顺着这条思路前,在章节2.1 准备阶段中,就可以看到对标签的解析,那就从这开始吧。

// 该方法在 XMLConfigBuilder 类中
private void pluginElement(XNode parent) throws Exception {
    if (parent != null) {// 判空
        // 遍历子节点 <plugin>
        for (XNode child : parent.getChildren()) {
            // 节点 interceptor 属性
            String interceptor = child.getStringAttribute("interceptor");
            // 子节点 <property>
            Properties properties = child.getChildrenAsProperties();
            // 反射创建插件对象
            Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance();
            // 给对象设置 properties 属性
            interceptorInstance.setProperties(properties);
            // 将插件添加到 configuration 的 interceptorChain 对象中
            configuration.addInterceptor(interceptorInstance);
        }
    }
}

// 该方法在 Configuration 类中
public void addInterceptor(Interceptor interceptor) {
    interceptorChain.addInterceptor(interceptor);
}

// 插件链 类,管理所有插件的类
public class InterceptorChain {
    // 存放插件的List
    private final List<Interceptor> interceptors = new ArrayList<>();
    // 插件执行方法,这里是全量遍历执行
    public Object pluginAll(Object target) {
        for (Interceptor interceptor : interceptors) {
            // 重点在这里,进去看看插件做了什么
            target = interceptor.plugin(target);
        }
        return target;
    }

    public void addInterceptor(Interceptor interceptor) {
        interceptors.add(interceptor);
    }

    public List<Interceptor> getInterceptors() {
        return Collections.unmodifiableList(interceptors);
    }

}

插件拦截器类 Interceptor

public interface Interceptor {
	// 最终用户自定义代理类要实现的逻辑
    Object intercept(Invocation invocation) throws Throwable;
	// 这是接口的默认方法实现
    default Object plugin(Object target) {
        // 最终调用到 Plugin 类的 wrap 方法
        return Plugin.wrap(target, this);
    }

    default void setProperties(Properties properties) {
        // NOP
    }

}

插件类 Plugin

// 一看,这个类实现了 InvocationHandler,就知道要用代理了
public class Plugin implements InvocationHandler {

    private final Object target; // 被代理的目标对象
    private final Interceptor interceptor;  // Mybatis的拦截器对象
    private final Map<Class<?>, Set<Method>> signatureMap; // @signature 注解的配置

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

    public static Object wrap(Object target, 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;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        try {
            Set<Method> methods = signatureMap.get(method.getDeclaringClass());
            if (methods != null && methods.contains(method)) {
                // 调用自定义插件 intercept 方法
                return interceptor.intercept(new Invocation(target, method, args));
            }
            return method.invoke(target, args);
        } catch (Exception e) {
            throw ExceptionUtil.unwrapThrowable(e);
        }
    }

    private static Map<Class<?>, Set<Method>> getSignatureMap(Interceptor interceptor) {
        // 插件类都需要有 Intercepts 注解
        Intercepts interceptsAnnotation = interceptor.getClass().getAnnotation(Intercepts.class);
        // issue #251
        if (interceptsAnnotation == null) { // Intercepts 注解不能为空
            throw new PluginException("No @Intercepts annotation was found in interceptor " + interceptor.getClass().getName());
        }
        // Intercepts 注解中 Signature注解的值(数组)
        Signature[] sigs = interceptsAnnotation.value();
        Map<Class<?>, Set<Method>> signatureMap = new HashMap<>();
        for (Signature sig : sigs) {
            Set<Method> methods = signatureMap.computeIfAbsent(sig.type(), k -> new HashSet<>());
            try {
                // 反射拿到 Signature注解配置中设置的类型下的方法
                Method method = sig.type().getMethod(sig.method(), sig.args());
                methods.add(method); // 添加到 map 中
            } catch (NoSuchMethodException e) {
                throw new PluginException("Could not find method on " + sig.type() + " named " + sig.method() + ". Cause: " + e, e);
            }
        }
        return signatureMap;
    }
    // 反射技术获取到类型 type的所有接口类,因为JDK反射技术需要接口
    private static Class<?>[] getAllInterfaces(Class<?> type, Map<Class<?>, Set<Method>> signatureMap) {
        Set<Class<?>> interfaces = new HashSet<>();
        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()]);
    }

}
  • 7
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

多栖码农

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值