美文网首页
Mybatis(六):myBatis插件原理

Mybatis(六):myBatis插件原理

作者: aix91 | 来源:发表于2019-02-25 10:12 被阅读0次

1. Mybatis能拦截的接口

myBatis可以对四大接口进行拦截:Executor, StatementHandler, ResultSetHandler, ParameterHandler


myBatis执行流程
  • Executor 是mybatis的内部执行器,它通过调用StatementHandler来操作数据库
  • StatementHandler是Mybatis直接和数据库执行sql脚本的对象。
  • ParameterHandler是Mybatis实现Sql入参设置的对象。插件可以改变我们Sql的参数默认设置。
  • ResultSetHandler是Mybatis把ResultSet集合映射成POJO的接口对象。我们可以定义插件对Mybatis的结果集自动映射进行修改。
    上面四个接口下的方法,就是mybatis插件允许拦截的方法。

2. Interceptor 接口

mybatis插件,必须要实现Interceptor接口;

public interface Interceptor {   
  Object intercept(Invocation invocation) throws Throwable;       
  Object plugin(Object target);    
  void setProperties(Properties properties);
}
  • plugin: 用来封装目标对象。可以返回目标对象本身,也可以根据实际需要,创建一个代理对象,进而实现自定义功能; 注意,不要随意创建plugin,只为需要拦截的接口创建plugin
    public Object plugin(Object target) {
        if (target instanceof StatementHandler || target instanceof ResultSetHandler) {
            return Plugin.wrap(target, this);
        } else {
            return target;
        }
    }
  • intercept: 进行拦截时,要执行的方法
  • setProperties方法是在Mybatis进行配置插件的时候可以配置自定义相关属性,即:接口实现对象的参数配置

3. 插件生成原理

首先我们来看看,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;
}

可以看到,四个接口对象在生成时,都会调用InterceptorChain.pluginAll()方法。这个方法会调用pluginChain,执行所有的plugin,生成对应的接口对象。所以,在实现plugin方法时判断一下目标类型,是本插件要拦截的对象才执行Plugin.wrap方法,否者直接返回目标本省,这样可以减少目标被代理的次数。

4. 配置注解@Intercepts

mybatis通过@Intercepts来配置拦截哪个对象的哪个拦截方法

@Intercepts({@Signature(
type = Executor.class, //拦截的类
method = "query", //拦截类的某个方法
args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})})// 对应方法的参数
public class TestInterceptor implements Interceptor {
}

5. spring项目中使用插件

在创建SqlSessionFactory时,插入Interceptor List

    @Bean
    public Interceptor myFirstPlugin() {
        return new MyFirstPlugin();
    }
    @Bean
    public Interceptor mySecondPlugin() {
        return new MySecondPlugin();
    }
     @Bean
    public SqlSessionFactoryBean sqlSessionFactory(@Qualifier("dataSource") DataSource dataSource) {
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(dataSource);
        Interceptor[] plugins = new Interceptor[]{mySecondPlugin(), myFirstPlugin()};
        sqlSessionFactoryBean.setPlugins(plugins);
        return sqlSessionFactoryBean;
    }

相关文章

网友评论

      本文标题:Mybatis(六):myBatis插件原理

      本文链接:https://www.haomeiwen.com/subject/szcjyqtx.html