mybatis插件开发原理及方法

本文围绕MyBatis插件展开,介绍了插件原理,即在四大对象创建时通过拦截器为目标对象创建代理对象实现AOP。还说明了插件编写步骤,包括实现Interceptor类、使用注解签名和注册到配置文件。此外,分析了源码和多个插件运行时产生多层代理的流程。

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

MyBatis在四大对象的创建过程中,都会有插件进行介入。

interceptorChain.pluginAll

 

public Object pluginAll(Object target) {

  for (Interceptor interceptor : interceptors) {

    target = interceptor.plugin(target);

  }

  return target;

}

插件原理

在四大对象创建的时候

1. 每个创建出来的对象不是直接返回的,而是

interceptorChain.pluginAll(parameterHandler);

2. 获取到所有的Interceptor(拦截器)(插件需要实现的接口);

调用interceptor.plugin(target);返回target包装后的对象

3. 插件机制,我们可以使用插件为目标对象创建一个代理对象;AOP(面向切面)

我们的插件可以为四大对象创建出代理对象;

代理对象就可以拦截到四大对象的每一个执行;

插件编写

1、编写Interceptor的实现类

2、使用@Intercepts注解完成插件签名

3、将写好的插件注册到全局配置文件中

源码

如我们写的MyFirstPlugin,其插件签名为:

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

 

那么将其纳入到我们mybatis查询的逻辑里面,是在BaseStatementHandler构造方法执行的时候,执行

this.parameterHandler = configuration.newParameterHandler(mappedStatement, parameterObject, boundSql);

 

在newParameterHandler时

parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);

 

调用到MyFirstPlugin的plugin方法

Object wrap = Plugin.wrap(o, this);

在wrap方法中,会进行判断是不是指定的type,如果是指定的,那么就创建一个代理对象

public static Object wrap(Object target, Interceptor interceptor) {



  if (interfaces.length > 0) {

    return Proxy.newProxyInstance(

        type.getClassLoader(),

        interfaces,

        new Plugin(target, interceptor, signatureMap));

  }

  return target;

}

注意:return Proxy.newProxyInstance(new Plugin)


之后返回到SimpleExecutor中执行prepareStatements方法

private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {

  Statement stmt;

  Connection connection = getConnection(statementLog);

  stmt = handler.prepare(connection, transaction.getTimeout());

  handler.parameterize(stmt);

  return stmt;

}

这个handler就是我们前面创建的代理对象。

在prepare和parameterize方法中,如果其为代理对象,那么会执行Plugin class的invoke方法

@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)) {

      return interceptor.intercept(new Invocation(target, method, args));

    }

    return method.invoke(target, args);

  } catch (Exception e) {

    throw ExceptionUtil.unwrapThrowable(e);

  }

}

如果method对应上了,那么就执行interceptor.intercept,去执行MyFirstPlugin的intercept接口

没有对应上,那么就直接放行

多个插件运行流程

当我定义两个相同的plugin的时候,

多个插件会产生多层代理的一个代理对象

原因就是:

public Object pluginAll(Object target) {

  for (Interceptor interceptor : interceptors) {

    target = interceptor.plugin(target);

  }

  return target;

}

target = interceptor.plugin(target);这句,由MyFirstPlugin包装好的代理对象再作为target,再生成一次target

调用Plugin的invoke方法的时候

@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)) {

      return interceptor.intercept(new Invocation(target, method, args));

    }

    return method.invoke(target, args);

  } catch (Exception e) {

    throw ExceptionUtil.unwrapThrowable(e);

  }

}

就会先调用MySecondFilter的intercept方法,再调用MyFirstFilter的intercept方法。

 

源码参考:https://gitee.com/constfafa/mybatis-spring-demo.git

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值