【Mybatis】mybatis插件源码分析

版权声明:本文为原创文章,转载时请注明出处;文章如有错漏,烦请不吝指正,谢谢! https://blog.csdn.net/reliveIT/article/details/50289395

一、基本概要

    参见mybatis官方文档:点击打开链接

    1. 快速开发; 

    2. 插件原理实现的主要技术和思想;

    3. 为什么插件只支持Executor、StatementHandler、ResultSetHandler、ParameterHandler四种类型?


二、快速开发

    mybatis插件开发极易上手,参见第一节中mybatis官方文档中的demo,只需要明白几个概念即可快速开发。

1. 官方示例

@Intercepts({ @Signature(type = Executor.class, method = "update", args = {
		MappedStatement.class, Object.class }) })
public class ExamplePlugin implements Interceptor {
	public Object intercept(Invocation invocation) throws Throwable {
		return invocation.proceed();
	}

	public Object plugin(Object target) {
		return Plugin.wrap(target, this);
	}

	public void setProperties(Properties properties) {
	}
}

2. 快速开发步骤

    1. 实现Interceptor接口;

    2. @Interceptor注解中的注意事项:

        a. type是Executor、StatementHandler、ResultSetHandler、ParameterHandler中的一个或多个;

        b. method为上述四个类型中定义的方法名;

        c. args为b中需要拦截的method的参数类型;

    3. 最后,在mybatis-config.xml中声明自定义开发的插件即可;

<plugins>
	<plugin interceptor="xx.xx.Xxxx" />
</plugins>

3. 注意事项

    mybatis插件开发会存在重复代理的情况,因此在做插件开发的时候,最好在Interceptor.plugin方法中使用Class.isAssignableFrom(Class)来进行判断,避免重复代理问题。

    至于为什么会产生重复代理,后续源码阅读时候会进一步分析。



三、插件支持类型

    如上文所述,mybatis插件开发只支持四种类型,Executor、StatementHandler、ResultSetHandler、ParameterHandler。之所以mybatis只支持上述四种类型,其关键在于一句源码:interceptorChain.pluginAll(executor)。以Executor源码为例,简要分析为什么mybatis只支持这四种类型。

1. 思想及源码

    1. 当new SqlSessionFactoryBuilder().builder(InputStream)的时候,mybatis会将mybatis-config.xml配置文件中的内容解析成Configuration对象,以便于运行时获取;


    2. 此时所有<plugins>标签中plugin会被实例化并注册到Configuration的属性InterceptorChain中,在InterceptorChain中实际上是一个ArrayList来保存拦截器;



    3. 当Mybatis启动完成后,SqlSessionFactory.openSession(),此时会通过配置中心Configuration实例化相应的Executor,在实例化的同时完成相应代理的生成;




    4. 上述步骤中,interceptor.plugin(Object target)实际上是需要我们来实现Interceptor接口的,在实现接口的plugin方法时,target为目标代理类,即四种类型之一,而plugin中的逻辑主要是通过Plugin.warp(target, interceptor)来完成,最终生成相应的JDK动态代理;


3. 其他三种类型概要

    至于其他三种类型,同理也是根据Mybatis配置中心生成相应对象的时候调用了interceptorChain.pluginAll方法。因此,Mybatis框架之所以只支持上述四种类型,其原因是框架中只有这四类对象生成的时候会条用该源码。



四、插件实现技术及思想

1. 思想

    如上文分析中所设计,实际上mybatis的插件技术基于JDK动态代理,但是在代理生成的过程中并不是对所有的方法都进行代理,因此这一部分的思想又体现成为:

    1. 不需要代理的方法,直接反射调用metho.invoke(target, args);

    2. 需要进行代理的方法,回调到拦截器Interceptor所提供的上下文场景中进行代理逻辑调用,并反射调用原方法逻辑;


    3. 为了将代理逻辑整合到插件中,Mybatis提供了Interceptor.intercept来提供代理逻辑环境,通过Invocation类来分离代理目标的原有逻辑,并在Plugin.invoke方法中对以上两种逻辑进行解耦和;

2. 源码分析

    承接上文分析,当Mybatis启动完成之后,插件对象相应的JDK动态代理对象也随之生成,此时调用代理的方法,将会转到InvocationHandler的invoke方法中执行,而在Mybatis插件源码中,实现InvocationHandler接口的正是Plugin类。


    此时调用代理目标对象将会进入invoke方法中执行。


    经过以上解耦和后,转到需要我们实现的Interceptor接口中执行代理逻辑和元逻辑。



五、再议重复代理

    实际上第一次看源码的时候,我发现如果有多个代理,则会重复的调用Interceptor.plugin方法,因此怀疑因此会导致重复代理,即目标对象的代理对象再次进行代理,有几个插件实例,则会进行重复代理几次。

    但是实际上是不会导致重复代理的,但是会导致有些不必要的方法重复调用,因此最好在Interceptor的plugin方法中加入2.3部分判断逻辑。


    如图所示,在进行代理生成之前,需要获取代理对象实现的接口,在该方法的逻辑中实际上是通过target.class.getInterfaces,因此不同的target得到的实现接口是不一样的,因此不会互导致重复代理问题。


附注:

    本文写的仓促,如有错漏,烦请不吝指正,谢谢!

猜你喜欢

转载自blog.csdn.net/reliveIT/article/details/50289395