mybatis插件源码理解

mybatis插件源码理解

最在使用mybatis,对这个插件的原理比较好奇,分享下自己的理解。

官方文档 https://mybatis.org/mybatis-3/zh/configuration.html#plugins

我就按照自己的理解按照2个部分,一个是配置解析的部分和调用的部分。

 

解析过程:

<!-- mybatis-config.xml -->
<plugins>
  <plugin interceptor="org.mybatis.example.ExamplePlugin">
    <property name="someProperty" value="100"/>
  </plugin>
</plugins>

以上图配置为例

解析过程比较简单,创建SqlSessionFactory时开始解析:

首先解析plugins的标签,然后解析里面的子标签plugin,然后解析interceptor标签,  在解析property。 根据interceptor和property创建插件对象。让后放入到全局配置变量configuration对象的interceptorChain属性中(就是个ArrayList)。

代码如下

private void pluginElement(XNode parent) throws Exception {
        if (parent != null) {
            // 遍历 <plugins /> 标签
            for (XNode child : parent.getChildren()) {
                String interceptor = child.getStringAttribute("interceptor");
                Properties properties = child.getChildrenAsProperties();
                // 创建 Interceptor 对象,并设置属性
                Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance();
                interceptorInstance.setProperties(properties);
                // 添加到 configuration 中
                configuration.addInterceptor(interceptorInstance);
            }
        }
    }

 

调用过程:

在调用openSession时(生成sqlSessionFactory时)会创建executor, 此时会遍历configuration中的interceptorChain中的所有interceptor,根据遍历出来的interceptor为当前创建executor(利用动态代理)包装多层代理对象Plugin(这个plugin包涵interceptor信息)。

public static Object wrap(Object target, Interceptor interceptor) {
        // 获得拦截的方法映射
        Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);
        // 获得目标类的类型
        Class<?> type = target.getClass();
        // 获得目标类的接口集合
        Class<?>[] interfaces = getAllInterfaces(type, signatureMap);
        // 若有接口,则创建目标对象的 JDK Proxy 对象
        if (interfaces.length > 0) {
            return Proxy.newProxyInstance(
                    type.getClassLoader(),
                    interfaces,
                    new Plugin(target, interceptor, signatureMap)); // 因为 Plugin 实现了 InvocationHandler 接口,所以可以作为 JDK 动态代理的调用处理器
        }
        // 如果没有,则返回原始的目标对象
        return target;
    }

所以在调用executor curd的时候,实际上调用的是做外层Plugin的invoke方法,  这个invoke会相判断当前的调用方法是否需要拦截,是就调用interceptor的intercept方法,不是就调用原方法。

如此一层一层调用下去(套娃)。

 

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)) {
                // 如果是,则拦截处理该方法
                return interceptor.intercept(new Invocation(target, method, args));
            }
            // 如果不是,则调用原方法
            return method.invoke(target, args);
        } catch (Exception e) {
            throw ExceptionUtil.unwrapThrowable(e);
        }
    }

 

此外:在mybatis在掉用我们的插件的intercept方法时,会传递个Invocation对象,这个对象包含了,target 包了代理对象的executor, 当前调用method的方法,args参数。这个Invocation对象包涵了一个proceed方法,用来直接调用method。

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

 

 

我的理解这个插件就是个套娃。。。。  如有错误欢迎指正。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值