Mybaits插件开发(PagerHelper原理、自定义插件)

本文围绕MyBatis插件展开,介绍了插件可改变或扩展MyBatis原有功能,通过继承Interceptor拦截器实现。还阐述了自定义慢查询插件的步骤,对插件模块源码进行分析,包括初始化、加载和调用。最后介绍了PagerHelper分页插件的使用步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

插件价绍

插件是用来改变或者扩展 mybatis 的原有的功能, mybaits 的插件就是通过继承Interceptor 拦截器实现的;
MyBatis 中能使用插件进行拦截的接口和方法如下:

在这里插入图片描述

自定义插件

自定义慢查询插件,记录项目中执行慢的Sql

@Intercepts({
	@Signature(type=StatementHandler.class,method="query",
               args={Statement.class, ResultHandler.class})
//	@Signature(type=StatementHandler.class,method="query",
//    		   args={MappedStatement.class,Object.class, RowBounds.class, ResultHandler.class})
})

public class ThresholdInterceptor implements Interceptor {
	
	private long threshold;

	@Override
	public Object intercept(Invocation invocation) throws Throwable {
		long begin = System.currentTimeMillis();
		Object ret = invocation.proceed();
		long end=System.currentTimeMillis();
		long runTime = end - begin;
		if(runTime>=threshold){
			Object[] args = invocation.getArgs();
			Statement stat = (Statement) args[0];
			MetaObject metaObjectStat = SystemMetaObject.forObject(stat);
			PreparedStatementLogger statementLogger = 
                					(PreparedStatementLogger)metaObjectStat.getValue("h");
			Statement statement = statementLogger.getPreparedStatement();
			System.out.println("sql语句:“"+statement.toString()+"”执行时间为:"
                               							+runTime+"毫秒,已经超过阈值!");
		}
		return ret;
	}

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

	@Override
	public void setProperties(Properties properties) {
		this.threshold = Long.valueOf(properties.getProperty("threshold"));
	}

}

插件实现步骤:

(1) 实现 Interceptor 接口方法

MyBatis 插件的实现必须实现 Interceptor 接口, 该接口有如下三个方法:

public interface Interceptor {
  
  //执行拦截逻辑的方法
  Object intercept(Invocation invocation) throws Throwable;

  //target是被拦截的对象,它的作用就是给被拦截的对象生成一个代理对象
  Object plugin(Object target);

  //读取在plugin中设置的参数
  void setProperties(Properties properties);

}

(2) 确定拦截的签名

插件拦截的接口,方法及方法参数

在这里插入图片描述

(3) 在核心配置文件中配置插件

在这里插入图片描述

插件模块源码分析

插件模块的源码分析主要搞清楚初始化、 插件加载以及插件如何调用三个问题;

1)插件初始化

插件的初始化实际是在 Mybatis 第一个阶段初始化的过程中加载到 Configuration 对象中的
具体代码见: XMLConfigBuilder.pluginElement, 如下图所示:

在这里插入图片描述

在 configuration 对象中, 使用 interceptorChain 类属性保存所有的插件,
interceptorChain类中有个 List 用于顺序保存所有的插件;

2)插件的加载

为什么插件可以拦截 Executor、 StatementHandler、 ParameterHandler、 ResultSetHandler四个接口指定的方法呢?
那是因为通过 configuration 对象创建这四大对象时, 通过责任链模式按插件的顺序对四大对象进行了增强;

Configuration.newExecutor

在这里插入图片描述

Configuration.newStatementHandler

在这里插入图片描述

Configuration.newParameterHandler

在这里插入图片描述

Configuration.newResultSetHandler

在这里插入图片描述

3)插件的调用

插件加载是通过 ExamplePlugin.plugin(Object)来增强的,通过上面分析的interceptorChain.pluginAll方法进入会来到该方法
Plugin 方法内部一般使用Plugin.wrap(target, this)来对四大对象进行增强,
Plugin.wrap 方法代码如下:

  //静态方法,用于帮助Interceptor生成动态代理
  public static Object wrap(Object target, Interceptor interceptor) {
	//解析Interceptor上@Intercepts注解得到的signature信息
    Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);
    Class<?> type = target.getClass();//获取目标对象的类型
    获取目标对象实现的接口(拦截器可以拦截4大对象实现的接口)
    Class<?>[] interfaces = getAllInterfaces(type, signatureMap);
    if (interfaces.length > 0) {
      //使用jdk的方式创建动态代理
      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)) {
        //如果当前方法需要被拦截,则调用interceptor.intercept方法进行拦截处理
        return interceptor.intercept(new Invocation(target, method, args));
      }
      //如果当前方法不需要被拦截,则调用对象自身的方法
      return method.invoke(target, args);
    } catch (Exception e) {
      throw ExceptionUtil.unwrapThrowable(e);
    }
  }

很明显, 归根究底 MyBatis 的插件是通过动态代理对原有的对象进行增强的

PagerHelper分页插件

使用步骤

1) 导入PageHelper的坐标

<dependency>
			<groupId>com.github.pagehelper</groupId>
			<artifactId>pagehelper</artifactId>
			<version>5.1.4</version>
		</dependency>
		
		<dependency>
			<groupId>com.fasterxml.jackson.core</groupId>
			<artifactId>jackson-databind</artifactId>
			<version>2.9.5</version>
		</dependency>

2)在Mybatis核心配置文件中配置插件

 	<plugins>	
  		 <plugin interceptor="com.github.pagehelper.PageInterceptor">
             <!--指定方言-->
            <property name="dialect" value="mysql"/>
		</plugin>
	</plugins>

3)测试分页数据

		Page<TUser> startPage = PageHelper.startPage(2, 3);
		List<TUser> list2 = mapper.selectByEmailAndSex2(email, sex);
		System.out.println(list2.size());

文档参考:https://github.com/pagehelper/Mybatis-PageHelper/blob/master/wikis/zh/HowToUse.md

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值