Mybatis插件开发

一、Mybatis插件介绍:

Mybatis插件又称拦截器,Mybatis采用责任链模式,通过动态代理组织多个插件(拦截器),通过这些插件可以改变Mybatis的默认行为(诸如SQL重写之类的)。Mybatis的插件是采用对四大接口的对象生成动态代理对象的方法来实现的。那么现在我们看下Mybatis是怎么创建这四大接口对象的。
在这里插入图片描述

public Executor newExecutor(Transaction transaction, ExecutorType executorType) { 
  //确保ExecutorType不为空(defaultExecutorType有可能为空) 
  executorType = executorType == null ? defaultExecutorType : executorType; 
  executorType = executorType == null ? ExecutorType.SIMPLE : executorType; 
  Executor executor;  
  if (ExecutorType.BATCH == executorType) { 
   executor = new BatchExecutor(this, transaction); 
  } else if (ExecutorType.REUSE == executorType) { 
   executor = new ReuseExecutor(this, transaction); 
  } else { 
   executor = new SimpleExecutor(this, transaction); 
  }  if (cacheEnabled) { 
   executor = new CachingExecutor(executor); 
  } 
  executor = (Executor) interceptorChain.pluginAll(executor); 
  return executor; 
} 
 
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) { 
  StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql); 
  statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler); 
  return statementHandler; 
} 
 
public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) { 
  ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql); 
  parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler); 
  return parameterHandler; 
} 
 
public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, RowBounds rowBounds, ParameterHandler parameterHandler, ResultHandler resultHandler, BoundSql boundSql) { 
  ResultSetHandler resultSetHandler = new DefaultResultSetHandler(executor, mappedStatement, parameterHandler, resultHandler, boundSql, rowBounds); 
  resultSetHandler = (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler); 
  return resultSetHandler; 
} 

查看源码可以发现, Mybatis框架在创建好这四大接口对象的实例后,都会调用InterceptorChain.pluginAll()方法。InterceptorChain对象是插件执行链对象,看源码就知道里面维护了Mybatis配置的所有插件(Interceptor)对象。

// target --> Executor/ParameterHandler/ResultSetHander/StatementHandler 
public Object pluginAll(Object target) { 
  for (Interceptor interceptor : interceptors) { 
   target = interceptor.plugin(target); 
  } 
  return target; 
} 

其实就是按顺序执行我们插件的plugin方法,一层一层返回我们原对象(Executor/ParameterHandler/ResultSetHander/StatementHandler)的代理对象。当我们调用四大接口的方法的时候,实际上是调用代理对象的相应方法,代理对象又会调用四大接口的实例。

二、自定义Mybatis插件:拦截增、删、改时的操作,进行事务校验

mybatis插件接口类:
package org.apache.ibatis.plugin;

import java.util.Properties;

/**
 * @author Clinton Begin
 */
public interface Interceptor {

  Object intercept(Invocation invocation) throws Throwable;

  Object plugin(Object target);

  void setProperties(Properties properties);

}

自定义的插件抽象类(这个可以不要)
/**
 * MyBatis插件抽象类
 *
 * @author xx
 * @CreateDate xxx
 */
public class AbstractInterceptor implements Interceptor {


    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        return invocation.proceed();
    }

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

    @Override
    public void setProperties(Properties properties) {

    }
}
自定义的插件类
/**
 * 拦截增、删、改时的操作 <br>
 *
 * @author xxx <br>
 * @CreateDate xxx <br>
 */
@Intercepts({
        @Signature(type = Executor.class, method = "update", args = {
                MappedStatement.class, Object.class
        })
})
public class TransactionVerifyInterceptor extends AbstractInterceptor {

    /**
     * 事务验证
     */
    private boolean transactionVerify;

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        if (transactionVerify && !TransactionSynchronizationManager.isSynchronizationActive() && needVerify(invocation)) {
            // 1、开启了事务验证,如果没有开启事务就抛出异常
            throw new NoTransactionException("You are modifying database data. Please open the transaction.");
        }
        return super.intercept(invocation);
    }

    @Override
    public void setProperties(Properties properties) {
        String verify = properties.getProperty(Settings.TRANSACTION_VERIFY);
        if (StringUtils.isNotEmpty(verify)) {
            transactionVerify = BooleanUtils.toBoolean(verify);
        }
    }

    private boolean needVerify(Invocation invocation) throws ClassNotFoundException {
        Object[] args = invocation.getArgs();
        MappedStatement mappedStatement = (MappedStatement) args[0];
        MapperMethodMapping mapperMethodMapping = MapperHelper.getMapperMethodMapping(mappedStatement);
        Method method = mapperMethodMapping.getMethod();
        IgnoreTransactionVerify ignore = AnnotationUtils.getAnnotation(method, IgnoreTransactionVerify.class);
        return ignore == null;
    }

}
类继承关系:

在这里插入图片描述

类似的插件还有分页插件

在这里插入图片描述

参考文章:https://database.51cto.com/art/201912/608017.htm#topx
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值