mybatis插件的执行顺序

原理

mybatis插件类型

plugins

MyBatis 允许你在映射语句执行过程中的某一点进行拦截调用。默认情况下,MyBatis 允许使用插件来拦截的方法调用包括:

  • Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
  • ParameterHandler (getParameterObject, setParameters)
  • ResultSetHandler (handleResultSets, handleOutputParameters)
  • StatementHandler (prepare, parameterize, batch, update, query)

那么 这些插件的执行顺序是如何的呢?

引入mybatis插件

maven依赖(略)

详见: gitee

配置插件

    <!-- 配置SqlSessionFactory对象 -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
       <!-- ..... -->
        <property name="configLocation" value="classpath:mybatis-config.xml"/>
    </bean>

自定义插件

插件的方法内容很简单,即打印当前方法的签名

 @Intercepts({
    @Signature(type = Executor.class, method = "query", args = { MappedStatement.class, Object.class, RowBounds.class,ResultHandler.class })
})
@Slf4j
public class Executor_Interceptor implements Interceptor {

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        log.info("---------------------"+this.getClass().getSimpleName() +"::"+invocation.getMethod().getName());
        return invocation.proceed();
    }

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

    }
}

同理,以此注入如下的插件:

// Executor2_Interceptor ,Executor3_Interceptor 同  Executor_Interceptor

//ParameterHandler 监听setParameters方法
@Intercepts({
    @Signature( type= ParameterHandler.class,method = "setParameters", args = {PreparedStatement.class})
})
public class ParameterHandler_Interceptor implements Interceptor  {}


//ResultSetHandler 监听 handleResultSets 方法
@Intercepts({
    @Signature( type= ResultSetHandler.class,method = "handleResultSets", args = {Statement.class}),
})
public class ResultSetHandler_Interceptor implements Interceptor {}


//StatementHandler 监听 prepare和query方法
@Intercepts({
    @Signature( type= StatementHandler.class,method = "prepare", args = {Connection.class,Integer.class}),
    @Signature( type= StatementHandler.class,method = "query", args = {Statement.class,ResultHandler.class})
})
public class StatementHandler_Interceptor implements Interceptor {}

同类型插件的执行顺序

mybatis-config.xml

<?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>
    <plugins>
        <plugin interceptor="cn.jhs.framework.mybatis.plugins.Executor2_Interceptor"></plugin>
        <plugin interceptor="cn.jhs.framework.mybatis.plugins.Executor_Interceptor"></plugin>
        <plugin interceptor="cn.jhs.framework.mybatis.plugins.Executor3_Interceptor"></plugin>
      </plugin>
    </plugins>
</configuration>

执行查询方法–日志
在这里插入图片描述

结论
相同类型的plugins:按照 注册的 逆序,执行。

不同类型插件的执行顺序

mybatis-config.xml

<plugins>
   <plugin interceptor="cn.jhs.framework.mybatis.plugins.Executor_Interceptor"></plugin>
   <plugin interceptor="cn.jhs.framework.mybatis.plugins.ParameterHandler_Interceptor"></plugin>
   <plugin interceptor="cn.jhs.framework.mybatis.plugins.ResultSetHandler_Interceptor"></plugin>
   <plugin interceptor="cn.jhs.framework.mybatis.plugins.StatementHandler_Interceptor"></plugin>
</plugins>

执行查询方法–日志
在这里插入图片描述

猜测
不同类型的plugins:按照 是否注册的顺序影响呢?

随机顺序-不同plugin

  <plugins>
        <plugin interceptor="cn.jhs.framework.mybatis.plugins.ResultSetHandler_Interceptor"></plugin>
        <plugin interceptor="cn.jhs.framework.mybatis.plugins.ParameterHandler_Interceptor"></plugin>
        <plugin interceptor="cn.jhs.framework.mybatis.plugins.StatementHandler_Interceptor"></plugin>
        <plugin interceptor="cn.jhs.framework.mybatis.plugins.Executor_Interceptor"></plugin>
    </plugins>

此时的执行结果,跟上述结果相同

结论
总体顺序
Executor -> StatementHandler ->ParameterHandler -> ResultSetHandler
但是部分-方法的顺序,不受上述规则的控制。如日志中的StatementHandler.prepare()和StatementHandler.query()



源码分析

我们都知道,XXMapper#selectXXX(); 转换为 DefaultSqlSession#selectList(); 下面从DefaultSqlSession入手,分析,上述插件的执行顺序的原理。

构建DefaultSqlSession — 初始化


--DefaultSqlSessionFactory#openSessionFromDataSource()
----configuration#newExecutor(); 				//1.	创建 executor
------executor = new SimpleExecutor(); 			//1.1	创建SimpleExecutor
------executor = new CachingExecutor(executor);	//1.2	包装成: CachingExecutor
------executor = (Executor) interceptorChain.pluginAll(executor); 	//1.3	使用 interceptorChain.代理executor
----------for-each -> interceptor.plugin(target); 					//1.3.1 内部调用: Plugin.wrap(target, this);
----------Plugin#wrap(target, this); 								//1.3.1.1 生成executor代理对象
  public static Object wrap(Object target, Interceptor interceptor) {
  	//a. 获取代理interceptor所有签名的<接口,Set<接口methods>>
    Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);
    //b. target = CachingExecutor;
    Class<?> type = target.getClass();
    //c. type(CachingExecutor.class)与interceptor签名接口 交集;  
    // 此时CachingExecutor实现了Executor接口, 故只有@Signature中包含Executor接口的interceptor,会生成代理对象;
    Class<?>[] interfaces = getAllInterfaces(type, signatureMap);

    //d. 如果存在交集, 则生成代理对象
    if (interfaces.length > 0) {
      return Proxy.newProxyInstance(
          type.getClassLoader(),
          interfaces,
          new Plugin(target, interceptor, signatureMap));
    }

    //e. 如果不存在交集,则返回原始对象
    return target;
  }
------返回一个可能存在多重Plugin..warp的(CachingExecutor(SimpleExecutor)) 对象;
-----new DefaultSqlSession(configuration, executor, autoCommit);	// 2.  构造DefaultSqlSession对象

此时构建出一个DefaultSqlSession对象,它的内容如下:

public class DefaultSqlSession implements SqlSession {
	private Configuration configuration;
  	private Executor executor; // 仅包含 plugins中,@Signature中包含Executor接口的interceptor

  	public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
	  MappedStatement ms = configuration.getMappedStatement(statement);
      return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
  	}
} 

执行查询

接上一节内容,所有的查询都会转换为executor.query, 此时的executor为@Signature中包含Executor接口的interceptor生成的Plugin代理对象

1. 执行Plugin<Executor.class>

Plugin执行代理方法的逻辑如下。 此时的Plugin代理的是Executor

  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      Set<Method> methods = signatureMap.get(method.getDeclaringClass());    //1.1 查看当前Plugin支持的方法类型set
      if (methods != null && methods.contains(method)) {					 //1.2 检验执行mapper方法类型,与当前Plugin设置的方法 是否一致
        return interceptor.intercept(new Invocation(target, method, args));  //1.3. 链式调用*执行插件interceptor.intercept方法
      }
      return method.invoke(target, args); //1.4 执行原始对象 CachingExecutor();
  }
2. 执行CachingExecutor#query()

上一步执行完成之后,最终执行真实对象查询方法CachingExecutor#query.

-------delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql); 
	  //delegate = SimpleExecutor 
	  //SimpleExecutor extends BaseExecutor
	  //delegate.query() 执行 BaseExecutor#query(), 而BaseExecutor内部调用抽象方法doQuery,由 SimpleExecutor实现
---------SimpleExecutor#doQuery(); // 2.1
-----------StatementHandler handler = configuration.newStatementHandler() // 2.1.1
-------------StatementHandler statementHandler = new RoutingStatementHandler(); //2.1.1.1 构造 RoutingStatementHandler
---------------delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql); //2.1.1.1.1 内部delegate
				//delegate 构造内容
				public class PreparedStatementHandler extends BaseStatementHandler {
				 protected BaseStatementHandler() { 
					    this.parameterHandler = configuration.newParameterHandler(); //a. 内部调用创建 ParameterHandler
					    this.resultSetHandler = configuration.newResultSetHandler(); //b. 内部调用创建 ResultSetHandler
					  }
				}
-------------statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler); //2.1.1.2 最终通过Plugin.warp(=RoutingStatementHandler);它的delegate类型为PreparedStatementHandler
-----------Statement stmt = prepareStatement(handler, ms.getStatementLog());// 2.1.2
-------------Connection connection = getConnection(statementLog); 			// 2.1.2.1 获取数据库连接
-------------stmt = handler.prepare(connection, transaction.getTimeout());	// 2.1.2.2 PreparedStatementHandler->BaseStatementHandler#prepare ;
-------------handler.parameterize(stmt);									// 2.1.2.3 PreparedStatementHandler#parameterize ; 

-----------return handler.<E>query(stmt, resultHandler);// 2.1.3
------------PreparedStatementHandler#query()
 public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    PreparedStatement ps = (PreparedStatement) statement;
    ps.execute(); //2.1.3.1 执行ps
    return resultSetHandler.<E> handleResultSets(ps); //2.1.3.2  resultSetHandler 渲染执行结果
  }

关键类图
在这里插入图片描述

执行顺序

  • handler.prepare : StatementHandler
  • handler.parameterize: StatementHandler + ParameterHandler
  • handler.<E>query : StatementHandler + ParameterHandler
  • resultSetHandler.<E> handleResultSets(ps) : ResultSetHandler
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值