MyBatis源码阅读——MyBatis插件原理

前言

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

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

这些类中方法的细节可以通过查看每个方法的签名来发现,或者直接查看 MyBatis 发行包中的源代码。 如果你想做的不仅仅是监控方法的调用,那么你最好相当了解要重写的方法的行为。 因为如果在试图修改或重写已有方法的行为的时候,你很可能在破坏 MyBatis 的核心模块。 这些都是更低层的类和方法,所以使用插件的时候要特别当心。

使用插件

功能描述:编写一个MyBatis插件,在执行删除动作的时候,做日志记录

实现过程:

编写类DeleteWarningPlugin,实现Interceptor 接口

@Intercepts({@Signature(
        type = Executor.class,
        method = "update",
        args = {MappedStatement.class, Object.class})})
public class DeleteWarningPlugin implements Interceptor {

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        MappedStatement ms = (MappedStatement) invocation.getArgs()[0];
        System.out.println();
        if ("DELETE".equals(ms.getSqlCommandType().name())) {
            SqlSource sqlSource = ms.getSqlSource();
            RawSqlSource rawSqlSource = (RawSqlSource) sqlSource;
            MetaObject metaObject = SystemMetaObject.forObject(rawSqlSource);
            SqlSource thisSqlSource = (StaticSqlSource) metaObject.getValue("sqlSource");
            MetaObject metaObjectSql = SystemMetaObject.forObject(thisSqlSource);
            String sql = (String) metaObjectSql.getValue("sql");
            System.out.println("系统发生删除操作:" + sql);
        }
        return invocation.proceed();
    }

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

    @Override
    public void setProperties(Properties properties) {
        for (String key : properties.stringPropertyNames()) {
            System.out.println(key + " " + properties.get(key));
        }
    }
}

在mybatis-config.xml配置中加入插件

 <plugins>
        <plugin interceptor="top.yuyufeng.learn.mybatis.interceptor.DeleteWarningPlugin">
            <property name="someProperty" value="100"/>
        </plugin>
        <plugin interceptor="com.github.pagehelper.PageHelper">
            <property name="dialect" value="mysql"/>
        </plugin>
    </plugins>

运行调试:

public class Demo6PluginExample {
    public static void main(String[] args) throws IOException {
        String resource = "mybatis/conf/mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        //从 XML 中构建 SqlSessionFactory
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        SqlSession session = sqlSessionFactory.openSession(true);
        try {
            BlogMapper mapper = session.getMapper(BlogMapper.class);
            int result = mapper.delete(25L);
            System.out.println("result: " + result);
        } finally {
            session.close();
        }
    }
}

调试结果:

someProperty 100
系统发生删除操作:delete from blog where blog_id = ?
result: 1

可以看到,插件已经起了效果。

插件原理

我们拿Executor 来说,
这里写图片描述
打开源码org.apache.ibatis.session.Configuration->newExecutor()

  public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
    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;
  }

打开InterceptorChain源码

public class InterceptorChain {

  private final List<Interceptor> interceptors = new ArrayList<Interceptor>();

  public Object pluginAll(Object target) {
  //迭代包装所有插件
    for (Interceptor interceptor : interceptors) {
      target = interceptor.plugin(target);
    }
    return target;
  }

  public void addInterceptor(Interceptor interceptor) {
    interceptors.add(interceptor);
  }

  public List<Interceptor> getInterceptors() {
    return Collections.unmodifiableList(interceptors);
  }

}

猜你喜欢

转载自blog.csdn.net/qq_18860653/article/details/80654087