在上一篇中Mybatis插件原理分析(一)中我们主要介绍了一下Mybatis插件相关的几个类的源码,并对源码进行了一些解释,接下来我们通过一个简单的插件实现来对Mybatis插件的运行流程进行分析。

一、简单的插件MyInterceptor,源码如下:

/**
 * 实现Interceptor的类必须使用注解@Intercepts,Plugin类中的getSignatureMap函数就是来解析这个注解
 * 获得注解中的相关信息,比如拦截的method,拦截的接口实现类,以及method的函数参数等
 */
@Intercepts( {
@Signature(method = "query", type = Executor.class, args = {
MappedStatement.class, Object.class, RowBounds.class,
ResultHandler.class })})
public class MyInterceptor implements Interceptor {

	//这个函数的最终结果是调用invocation.proceed()方法,调用目标类的method.invoke方法
	@Override
	public Object intercept(Invocation invocation) throws Throwable {
		return invocation.proceed();
	}

	//这个方法的目的就是将目标类通过代理类Plugin的wrap方法来生成目标类
	@Override
	public Object plugin(Object target) {

		return Plugin.wrap(target, this);
	}

	@Override
	public void setProperties(Properties properties) {
		// TODO Auto-generated method stub

	}

}

二、mybatis的配文件中添加如下配置:

<plugins>
      <plugin interceptor="com.tianjunwei.MyInterceptor"></plugin>
</plugins>

这样就完成了一个简单插件的实现工作。

三、运行流程

(1)在mybatis启动阶段,在Configuration类中的Executor、ParameterHandler、ResultSetHandler和StatementHandler的实现类初始化过程中会调用如下进行初始化:

resultSetHandler = (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler);

在InterceptorChain类中调用pluginAll函数,会调用所有已经实现的Interceptor,例如我们刚实现的MyInterceptor类的plugin方法

public Object pluginAll(Object target) {
    for (Interceptor interceptor : interceptors) {
      target = interceptor.plugin(target);
    }
    return target;
  }

MyInterceptor类中的plugin方法其实调用的是Plugin的wrap函数,wrap函数是用来生代理类的目标类的

	//这个方法的目的就是将目标类通过代理类Plugin的wrap方法来生成目标类
	@Override
	public Object plugin(Object target) {

		return Plugin.wrap(target, this);
	}

Plugin类中的wrap实现如下,其目的就是生成目标类,这样Executor、ParameterHandler、ResultSetHandler和StatementHandler的实现类都是目标类

//一个静态方法,对一个目标对象进行包装,生成目标类。
  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);
	//如果长度为>0 则返回代理类 否则不做处理
    if (interfaces.length > 0) {
	  //创建JDK动态代理对象
      return Proxy.newProxyInstance(
          type.getClassLoader(),
          interfaces,
          new Plugin(target, interceptor, signatureMap));
    }
    return target;
  }

我们以Executor的实现类来说明,当调用Executor的query方法是,根据代理类机制会调用Plugin的invoke方法

在invoke方法中我们可以看,首先会判断目标类调用的方法是否是我们在实现的MyInterceptor的注解中进行了配置,如果配置了则会调用interceptor.intercept(new Invocation(target, method, args));,这样的话就是调用的MyInterceptor的intercept方法,否则则直接调用return method.invoke(target, args);,

 //在执行Executor、ParameterHandler、ResultSetHandler和StatementHandler的实现类的方法时会调用这个方法
  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    try {
	  //通过method参数定义的类 去signatureMap当中查询需要拦截的方法集合
      Set<Method> methods = signatureMap.get(method.getDeclaringClass());
	  //判断是否是需要拦截的方法,如果需要拦截的话就执行实现的Interceptor的intercept方法,执行完之后还是会执行method.invoke方法,不过是放到interceptor实现类中去实现了
      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);
    }
  }

因为在MyInterceptor的plugin方法中已经把MyInterceptor作为参数赋值给Plugin了,代码如下

//这个方法的目的就是将目标类通过代理类Plugin的wrap方法来生成目标类
	@Override
	public Object plugin(Object target) {

		return Plugin.wrap(target, this);
	}

总结:Mybatis的插件的原理就是对Executor、ParameterHandler、ResultSetHandler和StatementHandler的实现类通过Plugin代理来生成目标类,并且关联生成的目标类与实现的Interceptor类,这样在调用Executor、ParameterHandler、ResultSetHandler和StatementHandler的实现类方法时会调用Plugin类中的invoke方法,在调用invoke方法时会进行判断这个实现类是否需要我们实现的MyInterceptor来处理,如果是会调用MyInterceptor的interceptor方法,进行一些处理之后调用Invocation的proceed方法,其实际处理方法也是调用method.invoke方法,如果不需要MyInterceptor处理时则直接调用method.invoke方法,Mybatis巧妙的利用的JDK的代理机制,实现了对目标类和方法在调用之前的一些处理,提高了整个框架的灵活性。

Mybatis插件原理分析(二)的更多相关文章

  1. Mybatis插件原理分析(一)

    我们首先介绍一下Mybatis插件相关的几个类,并对源码进行了简单的分析. Mybatis插件相关的接口或类有:Intercept.InterceptChain.Plugin和Invocation,这 ...

  2. Mybatis插件原理分析(三)分页插件

    在Mybatis中插件最经常使用的是作为分页插件,接下来我们通过实现Interceptor来完成一个分页插件. 虽然Mybatis也提供了分页操作,通过在sqlSession的接口函数中设置RowBo ...

  3. Mybatis框架(8)---Mybatis插件原理

    Mybatis插件原理 在实际开发过程中,我们经常使用的Mybaits插件就是分页插件了,通过分页插件我们可以在不用写count语句和limit的情况下就可以获取分页后的数据,给我们开发带来很大 的便 ...

  4. mybatis 插件原理

    [传送门]:mybatis 插件原理

  5. MyBATIS插件原理第一篇——技术基础(反射和JDK动态代理)(转)

    在介绍MyBATIS插件原理前我们需要先学习一下一些基础的知识,否则我们是很难理解MyBATIS的运行原理和插件原理的. MyBATIS最主要的是反射和动态代理技术,让我们首先先熟悉它们. 1:Jav ...

  6. (转载)Java NIO:NIO原理分析(二)

          NIO中的两个核心对象:缓冲区和通道,在谈到缓冲区时,我们说缓冲区对象本质上是一个数组,但它其实是一个特殊的数组,缓冲区对象内置了一些机制,能够跟踪和记录缓冲区的状态变化情况,如果我们使用 ...

  7. Redis有序集内部实现原理分析(二)

    Redis技术交流群481804090 Redis:https://github.com/zwjlpeng/Redis_Deep_Read 本篇博文紧随上篇Redis有序集内部实现原理分析,在这篇博文 ...

  8. Android 4.4 KitKat NotificationManagerService使用具体解释与原理分析(二)__原理分析

    前置文章: <Android 4.4 KitKat NotificationManagerService使用具体解释与原理分析(一)__使用具体解释> 转载请务必注明出处:http://b ...

  9. Mybatis 插件原理解析

    SqlSessionFactory 是 MyBatis 核心类之一,其重要功能是创建 MyBatis 的核心接口 SqlSession.MyBatis 通过 SqlSessionFactoryBuil ...

随机推荐

  1. Springboot项目maven多模块拆分

    多模块拆分的必要性 使用Java技术开发的工程项目,无论是数据处理系统还是Web网站,随着项目的不断发展,需求的不断细化与添加,工程项目中的代码越来越多,包结构也越来越复杂这时候工程的进展就会遇到各种 ...

  2. 15. 3Sum(中等)

    Given an array S of n integers, are there elements a, b, c in S such that a + b + c = 0? Find all un ...

  3. for循环&len函数和range函数的运用

     函数:len() 作用:返回字符串.列表.字典.元组等长度 语法:len(str) 参数: str:要计算的字符串.列表.字典.元组等 返回值:字符串.列表.字典.元组等元素的长度 实例 1.计算字 ...

  4. 安卓高级8 SurfaceView案例三 结合mediaplay播放视频

    我们知道mediaplay无法直接播放视频所以我们结合Surface package qianfeng.com.mediaplayerdemo; import android.media.MediaP ...

  5. Android简易实战教程--第三十九话《Chronometer实现倒计时》

    Android提供了实现按照秒计时的API,今天就是用这个API实现简单的倒计时. 来个布局: <?xml version="1.0" encoding="utf- ...

  6. Hive基本原理及环境搭建

    今天我主要是在折腾这个Hive,早上看了一下书,最开始有点凌乱,后面慢慢地发现,hive其实挺简单的,以我的理解就是和数据库有关的东西,那这样的话对我来说就容易多啦,因为我对sql语法应该是比较熟悉了 ...

  7. solr多集合配置

    1.1 多SolrCore配置 一个solr工程中可以配置多个SolrCore实例. 分享牛原创(尊重原创 转载对的时候第一行请注明,转载出处来自分享牛http://blog.csdn.net/qq_ ...

  8. python将nan, inf转为特定的数字

    最近,处理两个矩阵的点除,得到结果后,再作其他的计算,发现有些内置的函数不work:查看得到的数据,发现有很多nan和inf,导致python的基本函数运行不了,这是因为在除的过程中分母出现0的缘故. ...

  9. 网卡配置和DNS配置,手动挂在nas存储的共享目录,网络相关其它操作命令,修改防火墙中的端口配置,resolv.conf配置详细介绍和网卡信息配置详细介绍

    1.   网卡配置和DNS配置 若想服务器能够发邮件,需要让部署的服务器能够访问到外网环境.若部署的服务器访问不到外网,通过ping www.baidu.com的方式执行的时候,会出现以下问题: &q ...

  10. Python Tkinter小试

    前两天看到一篇关于Python使用Tkinter 的博文,写的很好.就拿来研究了一下,改了改.现分享如下: 参考 代码 # coding:utf8 # python2.73 winxp ''''' 天 ...