Mybatis最入门---分页查询(拦截器分页原理及实现)

[一步是咫尺,一步即天涯]

前文,我们演示了物理分页的Sql实现方式,这种方式使得我们每次在编写查询服务时,不断的重复造轮子。这样的代码实现方式就显得十分的笨拙了。本文是Mybatis分页查询的最后一片内容,我们将介绍基于拦截器的,精巧的实现方式。在阅读这篇文章之前,强烈建议各位看官能够先阅读上文。这样就能对下文我们提及的各种对象及他们之间的关系有一个清晰的关系。好了,废话不多讲,开始我们的正文部分吧。

准备工作:

a.操作系统 :win7 x64

b.基本软件:MySQL,Mybatis,SQLyog

-------------------------------------------------------------------------------------------------------------------------------------

【本文只作为原理分析及简单分页功能实现。实际生产环节,建议各位读者选择已经广泛应用的第三方jar包或平台性支持的实现】

-------------------------------------------------------------------------------------------------------------------------------------

1.创建本文我们将使用的工程Mybatis12,工程结构图如下:【重点文件我们给出,其他配置文件请读者参考前文工程】

2.管理分页的对象PagePOJO的具体内容如下:【这里我们给出基本分页的示例,更多需求请读者自行完成】

package com.csdn.ingo.entity;
/**
*@author 作者 E-mail:ingo
*@version 创建时间:2016年4月27日下午6:27:05
*类说明
*/
public class PagePOJO {
	private int totalNumber;//当前表中总条目数量
	private int currentPage;//当前页的位置
	private int totalPage;//总页数
	private int pageSize;//页面大小
	private int startIndex;//检索的起始位置
	private int totalSelect;//检索的总数目
	//...省略其他set,get方法
	public void setTotalNumber(int totalNumber) {
		this.totalNumber = totalNumber;
		this.count();
	}
	//...省略其他set,get方法

	public PagePOJO(int totalNumber, int currentPage, int totalPage, int pageSize, int startIndex, int totalSelect) {
		super();
		this.totalNumber = totalNumber;
		this.currentPage = currentPage;
		this.totalPage = totalPage;
		this.pageSize = pageSize;
		this.startIndex = startIndex;
		this.totalSelect = totalSelect;
	}
	public void count(){
		int totalPageTemp = this.totalNumber/this.pageSize;
		int plus = (this.totalNumber%this.pageSize)==0?0:1;
		totalPageTemp = totalPageTemp+plus;
		if(totalPageTemp<=0){
			totalPageTemp=1;
		}
		this.totalPage = totalPageTemp;//总页数

		if(this.totalPage<this.currentPage){
			this.currentPage = this.totalPage;
		}
		if(this.currentPage<1){
			this.currentPage=1;
		}
		this.startIndex = (this.currentPage-1)*this.pageSize;//起始位置等于之前所有页面输乘以页面大小
		this.totalSelect = this.pageSize;//检索数量等于页面大小
	}
}

3.新增单元测试方法,如下:【测试数据读者可以自行更换】

@Test
	public void testSelectPage() {
		try {
			// 创建分页对象
			//(int totalNumber, int currentPage, int totalPage, int pageSize, int startIndex, int totalSelect)
			PagePOJO page = new PagePOJO(5, 1, 1, 3, 1, 4);
			Map<String,Object> params = new  HashMap<String,Object>();
			params.put("page", page);
			UserInfoDao userInfo = sqlSession.getMapper(UserInfoDao.class);
			List<UserInfo> re = userInfo.selectByPage(params);
			System.out.println(re);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

-------------------------------------------------------------------------------------------------------------------------------------

上面与分页有关的,比较简单的内容我们先提供给大家。下面我们来详细的解释,拦截器实现分页的原理,各位看官睁大眼睛啊!

-------------------------------------------------------------------------------------------------------------------------------------

分页开始之前的问题:

  1. Mybatis如何找到我们新增的拦截服务。
  2. 自定义的拦截服务应该在什么时间拦截查询动作。即什么时间截断Mybatis执行流。
  3. 自定义的拦截服务应该拦截什么样的对象。不能拦截什么样的对象。
  4. 自定义的拦截服务拦截的对象应该具有什么动作才能被拦截。
  5. 自定义的拦截服务如何获取上下文中传入的参数信息。
  6. 如何把简单查询,神不知鬼不觉的,无侵入性的替换为分页查询语句。
  7. 最后,拦截器应该如何交还被截断的Mybatis执行流。

带着这些问题,我们来看看我们自定义的拦截服务是如何实现的。

-------------------------------------------------------------------------------------------------------------------------------------------------------

1.首先,我们看看查询语句的时序图,如下:

2.回顾一下,Mybatis允许我们能够进行切入的点,如下:【上文的运行细节图中也有描述,具体内容请参考前文。】

【老外其实从命名上已经给我们了充足的信息,我们结合时序图已经能够猜到其大概的含义及执行时间,鉴于篇幅的关系,具体的功能及作用,请参考前文详细内容。】

【结论】从上面的1.2两步,我们知道分页拦截的合理时机是在StatementHandler中。

3.现在,来看看这个StatementHandler的具体内容,如下:

【成员方法】

【类间关系】

这里请读者回顾上文中讲述的查询执行流,即这里将会执行prepare(Connection)方法,如下:

@Override
  public Statement prepare(Connection connection) throws SQLException {
    ErrorContext.instance().sql(boundSql.getSql());
    Statement statement = null;
    try {
      statement = instantiateStatement(connection);
      setStatementTimeout(statement);
      setFetchSize(statement);
      return statement;
    } catch (SQLException e) {
      closeStatement(statement);
      throw e;
    } catch (Exception e) {
      closeStatement(statement);
      throw new ExecutorException("Error preparing statement.  Cause: " + e, e);
    }
  }

执行到instantiateStatement(connection);如下:【PreparedStatementHandler中】

 @Override
  protected Statement instantiateStatement(Connection connection) throws SQLException {
    String sql = boundSql.getSql();
    if (mappedStatement.getKeyGenerator() instanceof Jdbc3KeyGenerator) {
      String[] keyColumnNames = mappedStatement.getKeyColumns();
      if (keyColumnNames == null) {
        return connection.prepareStatement(sql, PreparedStatement.RETURN_GENERATED_KEYS);
      } else {
        return connection.prepareStatement(sql, keyColumnNames);
      }
    } else if (mappedStatement.getResultSetType() != null) {
      return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
    } else {
      return connection.prepareStatement(sql);
    }
  }

【重点!!!】【这里我们就发现真正执行查询语句的地方connection.prepareStatement(...)】

由此,就解释了为什么在StatementHandler中进行拦截。

4.于是,我们可以得到拦截器的对应注解内容如下:【继承自Mybatis的Interceptor,必须实现3个方法,如下:】

@Intercepts({@Signature(type=StatementHandler.class,method="prepare",args={Connection.class})})

5.现在,自定义的拦截器的基本结构如下:

package com.csdn.ingo.interceptor;

/**
*@author 作者 E-mail:ingo
*@version 创建时间:2016年4月27日下午6:55:09
*/
@Intercepts({@Signature(type=StatementHandler.class,method="prepare",args={Connection.class})})
public class PageInterceptor implements Interceptor{

	/* (non-Javadoc)
	 * 拦截器要执行的方法
	 */
	public Object intercept(Invocation invocation) throws Throwable {
		//...
	}

	/* (non-Javadoc)
	 * 拦截器需要拦截的对象
	 */
	public Object plugin(Object target) {
		//...
	}

	/* (non-Javadoc)
	 * 设置初始化的属性值
	 */
	public void setProperties(Properties properties) {
		//...
	}

}

6.我们先看看拦截器上面基本结构中需要拦截的对象的具体方法实现:

/* (non-Javadoc)
	 * 拦截器需要拦截的对象,target。this,当前类的实例
	 */
	public Object plugin(Object target) {
		return Plugin.wrap(target, this);
	}

接下来,深入的看看wrap方法,如下:

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;
  }

接着,再看看getSignatureMap(interceptor);的详细内容,如下:

private static Map<Class<?>, Set<Method>> getSignatureMap(Interceptor interceptor) {
    Intercepts interceptsAnnotation = interceptor.getClass().getAnnotation(Intercepts.class);
    // ...

    return signatureMap;
  }

由此看到:annotation注解等其他相关内容都放在Map中返回。最后,判断出是否生成代理对象。

-------------------------------------------------------------------------------------------------------------------------------------

7.现在,我们已经成功的拦截到了目标对象,然后,就开始要改变查询过程了。在这里,出现了两个基本的问题

  • 如何获取原始的查询语句,即mapper文件中的sql语句。
  • 如何获取分页信息。

其实,在方法intercept(Invocation invocation)的参数中,已经包含了StatementHandler的信息,我们需要做的就是取出其中的信息,操作步骤如下:

a.先取出StatementHandler,如下:

StatementHandler statementHandler = (StatementHandler)invocation.getTarget();

·关于StatementHandler其有两个实现,【见上文】,默认情况下,程序会执行BaseStatementHandler下的内容。如下:

其中的mappedStatement中就包含着我们需要找的sql信息。如下:【如果读者已经看过前面的配置详解部分的内容,已经不会感到陌生】

现在,我们已经找到了我们想要的内容的具体位置,那我们应该怎样取出来呢?此时应该有看官大喊一声:这还不简单!可是我们来看看这部分的源码内容,如下:

protected final MappedStatement mappedStatement;

【注意】

其在BaseStatementHandler中是protected的,我们在没有继承等关系的条件下,是无法直接取出来的。

因此,我们就需要引入一个Mybatis已经实现了的对象:MetaObject。关于这个对象,可以先暂时的理解为帮助我们获取或设置该对象的原本不可访问的属性。

在3.3.1版本中,其内部细节如下:

这里,我们先只用到了public static MetaObject forObject(.....) ,其他内容有兴趣的读者可以自行学习。

于是,我们操作的对象就由StatementHandler的实例,变为MetaObject的实例,如下:

StatementHandler statementHandler = (StatementHandler)invocation.getTarget();
MetaObject metaObject = MetaObject.forObject(statementHandler, SystemMetaObject.DEFAULT_OBJECT_FACTORY, SystemMetaObject.DEFAULT_OBJECT_WRAPPER_FACTORY,new DefaultReflectorFactory());
		

现在对象已经获取了,就要获取其中的值了,在具体操作之前,我们再来梳理一下这里的类间关系,如下:【这里需要阅读过前文程序执行流程,或者之前已有了解】

现在,我们终于可以去获取其属性值了,获取的方法为参数名,OGNL表达式。具体内容如下:

MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("delegate.mappedStatement");
String id = mappedStatement.getId();

8.获得目标对象之后,现在要做进一步判断,即,当前被代理的对象是普通查询,还是分页查询。这里具体方式有很多,我们给出一种简单实现,更多实现及用法就行读者自行尝试吧。

如,我们在这里定义,id中(即mapper文件中sql语句的唯一id)以ByPage字符串结尾的,我们就认为该sql语句按照分页插叙来执行。具体的代码试下如下:

if(id.matches(".+ByPage$")){
     //....
}

-------------------------------------------------------------------------------------------------------------------------------------

9.通过上面一系列的步骤,我们已经成功的捕获了目标对象。接下来,我们就要开始加入我们“神不知鬼不觉‘”的代码了。

【具体分为下面的几个步骤】

  • 获取原始sql语句
  • 执行满足一定条件的查询,查询出结果总数,用于计算分页页面总数
  • 获取将dao层传入的分页参数
  • 将获取分页查询的参数,用于重新拼装分页查询语句。
  • 查询结束之后,交还程序执行流,退出拦截器

这里仍然需要前面执行流程的知识,默认情况下,执行的是PreparedStatementHandler。因此,我们可以从PreparedStatementHandler中寻找需要的sql语句。具体见上文第3步。这里仅给出省略内容,如下:

@Override
  protected Statement instantiateStatement(Connection connection) throws SQLException {
    String sql = boundSql.getSql();
    //...
  }

由此,我们发现Sql语句隐藏在boundSql中。回头看看,已经获得了的StatementHandler,其具体内容我们也在上文截图给大家。

于是,就得到了下面的代码:

BoundSql boundSql = statementHandler.getBoundSql();
Map<String,Object> params = (Map<String,Object>)boundSql.getParameterObject();

现在,各位看官可以尖叫了!!!所有的材料已经准备齐了。

-------------------------------------------------------------------------------------------------------------------------------------

10.查询总数对应的Mysql语句。【其他数据库原理一致,读者自行完成即可】

String sql = boundSql.getSql();
String countSql = "select count(*)from ("+sql+")a";

【注意:这里我们已经获得了传入参数与完整的sql语句,因此,select count还有更加高效的写法,请读者自行完成。】

11.接下来就是执行查询总数的语句。其总数的值用于完成分页页面数量控制。执行方法如下:

【回顾我们的注解,args配置】

Connection connection = (Connection) invocation.getArgs()[0];
//利用原始sql语句的方法执行
PreparedStatement countStatement = connection.prepareStatement(countSql);
//在本例中,查询参数为空,但实际应用时,多为带有条件的分页查询,下面的这句话就是获取查询条件的参数
ParameterHandler parameterHandler = (ParameterHandler) metaObject.getValue("delegate.parameterHandler");
//经过set方法,就可以正确的执行sql语句
parameterHandler.setParameters(countStatement);
ResultSet rs = countStatement.executeQuery();
//当结果集中有值时,表示页面数量大于等于1
if(rs.next()){
	page.setTotalNumber(rs.getInt(1));
}

到此,select count就执行完毕了。

11.分页的查询语句为:【其他数据库原理一致,读者自行完成即可】

String pageSql = sql+" limit "+page.getStartIndex()+","+page.getTotalSelect();

12.查询总数,分页查询的语句已经准备完成,如何替换原来的查询语句呢?再回头看看MetaObject对象,刚才,我们说它提供给我们获取或设置该对象的原本不可访问的属性。因此,就来利用它实现替换sql语句的功能。如下:【备注:这句话具体解释与上文getValue一致,请参考上文即可】

metaObject.setValue("delegate.boundSql.sql", pageSql);

到此,我们成功的把原有的简单查询语句替换为分页查询语句了,现在是时候将程序的控制权交还给Mybatis了,具体代码如下:

return invocation.proceed();

13.最后一步,自定义的拦截器需要交给Mybatis管理,这样才能使得Mybatis的执行与拦截器的执行结合在一起,即,拦截器需要注册到mybatis-config配置文件中。具体内容如下:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
	<properties resource="jdbc.properties"/>
	<settings>
		<setting name="logImpl" value="LOG4J"/>
	</settings>
	<typeAliases>
		<package name="com.csdn.ingo.entity"/>
	</typeAliases>
	<plugins>
		<plugin interceptor="com.csdn.ingo.interceptor.PageInterceptor"></plugin>
	</plugins>
	<environments default="development">
		<environment id="development">
			<transactionManager type="JDBC" />
			<dataSource type="POOLED">
				<property name="driver" value="${jdbc.driverClassName}" />
				<property name="url" value="${jdbc.url}" />
				<property name="username" value="${jdbc.username}" />
				<property name="password" value="${jdbc.password}" />
			</dataSource>
		</environment>
	</environments>
	<mappers>
		  <mapper resource="mappers/UserInfoMapper.xml"/>
	</mappers>
</configuration>

----------------------------------------------------------------------------------------------------------------------------------

上面是所有的原理内容,我们再给出完整的代码供大家学习

----------------------------------------------------------------------------------------------------------------------------------

14.PageInterceptor的完整内容如下:

package com.csdn.ingo.interceptor;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.Map;
import java.util.Properties;

import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.reflection.DefaultReflectorFactory;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.ReflectorFactory;
import org.apache.ibatis.reflection.SystemMetaObject;

import com.csdn.ingo.entity.PagePOJO;

/**
*@author 作者 E-mail:ingo
*@version 创建时间:2016年4月27日下午6:55:09
*类说明
*/
@Intercepts({@Signature(type=StatementHandler.class,method="prepare",args={Connection.class})})
public class PageInterceptor implements Interceptor{

	/* (non-Javadoc)
	 * 拦截器要执行的方法
	 */
	public Object intercept(Invocation invocation) throws Throwable {
		StatementHandler statementHandler = (StatementHandler)invocation.getTarget();
		MetaObject metaObject = MetaObject.forObject(statementHandler, SystemMetaObject.DEFAULT_OBJECT_FACTORY, SystemMetaObject.DEFAULT_OBJECT_WRAPPER_FACTORY,new DefaultReflectorFactory());
		MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("delegate.mappedStatement");
		String id = mappedStatement.getId();
		if(id.matches(".+ByPage$")){

			BoundSql boundSql = statementHandler.getBoundSql();
			Map<String,Object> params = (Map<String,Object>)boundSql.getParameterObject();
			PagePOJO page = (PagePOJO)params.get("page");
			String sql = boundSql.getSql();
			String countSql = "select count(*)from ("+sql+")a";
			Connection connection = (Connection) invocation.getArgs()[0];
			PreparedStatement countStatement = connection.prepareStatement(countSql);
			ParameterHandler parameterHandler = (ParameterHandler) metaObject.getValue("delegate.parameterHandler");
			parameterHandler.setParameters(countStatement);
			ResultSet rs = countStatement.executeQuery();
			if(rs.next()){
				page.setTotalNumber(rs.getInt(1));
			}
			String pageSql = sql+" limit "+page.getStartIndex()+","+page.getTotalSelect();
			metaObject.setValue("delegate.boundSql.sql", pageSql);
		}
		return invocation.proceed();
	}

	/* (non-Javadoc)
	 * 拦截器需要拦截的对象
	 */
	public Object plugin(Object target) {
		return Plugin.wrap(target, this);
	}

	/* (non-Javadoc)
	 * 设置初始化的属性值
	 */
	public void setProperties(Properties properties) {

	}
}

15.Mapper.xml文件中的sql语句如下:

	<select id="selectByPage" parameterType="Map" resultMap="UserInfoResult">
		select * from userinfo
	</select>

【注意:】

这里的sql语句就是简单查询

传入参数为Map,供多参,条件查询的等场景使用。【最好这么使用,因为拦截器只有1个】

16.执行单元测试方法,看看查询结果,如下:

如果看到类似上文输出,表明已经成功执行了分页查询!掌声!!!

----------------------------------------------------------------------------------------------------------------------------------

至此, Mybatis最入门---分页查询(拦截器分页原理及实现)结束

备注:

1.本文仅作为原理解释,实际应用时,建议各位看官最好使用更加严谨的第三方jar包,或者平台性的支持。

2.上文未给出的代码,在前文中均有给出,请读者自行查阅前文。

参考资料:

百度百科

特别感谢:http://blog.csdn.net/hupanfeng/article/details/9247379

时间: 2024-10-23 11:36:20

Mybatis最入门---分页查询(拦截器分页原理及实现)的相关文章

Mybatis最入门---动态查询(foreach)

[一步是咫尺,一步即天涯] 本文,我们来介绍使用Mybatis提供的<foreach>标签实现我们某些循环增改删差的需求.官方文档中的内容过于简陋,于是,博主筛选出比较全面讲述foreach用法的的内容,并且配有例子.希望各位看官能够手动敲一遍下面的例子,达到快速学习的目的. 准备工作: a.操作系统 :win7 x64 b.基本软件:MySQL,Mybatis,SQLyog -----------------------------------------------------------

Mybatis最入门---动态查询(if)

[一步是咫尺,一步即天涯] 前面我们花费很多篇幅来介绍核心元素<resultMap>的使用,在日常开发中,基本的静态的查询情况已经足够对付.但有些时候,我们想寻求一个能够根据参数自动调整SQL查询的方法.如,学校教导主任发现一个学生违反校纪校规,但是并没有当场抓住该学生,于是,他通过已经掌握的学生信息如[性别],[年纪],[身高]等信息来查询符合条件的学生.但是,这些信息的数量每次得到的数量都是不同的.换句话说,我们在设计系统时,无法确定传入参数的数量.也没有获得一个足以唯一确定这个对象的主键

Mybatis 拦截器实现原理

Mybatis 拦截器实现原理 标签(空格分隔): mybatis 拦截器概述 像springmvc一样,mybatis也提供了拦截器实现,只是他们拦截的对象不同. mybatis给Executor.StatementHandler.ResultSetHandler.ParameterHandler提供了拦截器功能, Executor提供了增删改查的接口. StatementHandler负责处理Mybatis与JDBC之间Statement的交互. ResultSetHandler负责处理St

【ssm】拦截器的原理及实现

一.背景: 走过了双11,我们又迎来了黑色星期五,刚过了黑五,双12又将到来.不管剁手的没有剁手的,估计这次都要剁手了!虽然作为程序猿的我,没有钱但是我们长眼睛了,我们关注到的是我们天猫.淘宝.支付宝之间的登录系统的关联,即只要我们在一个系统上登陆过了,在同门户的其他系统上就不用再次登陆了,这个涉及到的是我们的接下来下次要聊到的--SSO(单点登录):而这次我们要聊的,是我们的拦截器,因为只要我们没有登录,随便输入一个合法的地址涉及到订单或者隐私信息的内容时,就会被干到登录页面上去,这就是我们的

【SSM】拦截器的原理、实现

一.背景: 走过了双11,我们又迎来了黑色星期五,刚过了黑五,双12又将到来.不管剁手的没有剁手的,估计这次都要剁手了!虽然作为程序猿的我,没有钱但是我们长眼睛了,我们关注到的是我们天猫.淘宝.支付宝之间的登录系统的关联,即只要我们在一个系统上登陆过了,在同门户的其他系统上就不用再次登陆了,这个涉及到的是我们的接下来下次要聊到的--SSO(单点登录):而这次我们要聊的,是我们的拦截器,因为只要我们没有登录,随便输入一个合法的地址涉及到订单或者隐私信息的内容时,就会被干到登录页面上去,这就是我们的

MyBatis之拦截器分页

鲁春利的工作笔记,好记性不如烂笔头 数据库的分页主要有物理分页和逻辑分页.    物理分页:数据库本身提供的分页方式,如MySQL的limit.Oracle的rownum.SqlServer的top,好处是效率高,不好的地方就是不同数据库有不同的查询方式.    逻辑分页:从数据库将所有记录查询出来,存储到内存中,然后数据再直接从内存中获取并筛选分页,好处是能够统一查询方式,不好的地方是效率低,因为每次都要把全部数据查询出来再处理. 常用orm框架采用的分页技术:①:hibernate采用的是物

MyBatis 分页之拦截器实现

分页是WEB程序中常见的功能,mybatis分页实现与hibernate不同,相比hibernate,mybatis实现分页更为麻烦.mybatis实现分页需要自己编写(非逻辑分页RowBounds),以mysql为例,使用分页时需要自己在mapper中的sql语句中添加LIMIT #{xx},#{xx}.这种方式不具备可重用性与可拓展性. mybatis提供了plugins,plugins实现了拦截器的功能.它可以拦截指定类中的方法,当指定的方法被执行时,mybatis就会自动拦截并完成相应逻

mybatis拦截器分页

package com.test.interceptor; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.util.List; import java.util.Map; import java.util.Properties; import org.apache.ibatis.executor.Executor; import org.apache

由浅入深分析mybatis通过动态代理实现拦截器(插件)的原理

最近在用mybatis做项目,需要用到mybatis的拦截器功能,就顺便把mybatis的拦截器源码大致的看了一遍,为了温故而知新,在此就按照自己的理解由浅入深的理解一下它的设计. 和大家分享一下,不足和谬误之处欢迎交流.直接入正题. 首先,先不管mybatis的源码是怎么设计的,先假设一下自己要做一个拦截器应该怎么做.拦截器的实现都是基于代理的设计模式设计的,简单的说就是要创造一个目标类的代理类,在代理类中执行目标类的方法并拦截执行拦截器代码. 那么我们就用JDK的动态代理设计一个简单的拦截器