Mybatis常用用法之 Mybatis插件原理

678 阅读3分钟

这是我参与更文挑战的第3天,活动详情查看: 更文挑战

一:背景

一般情况下,开源框架都会提供插件或其他形式的拓展点,供开发者自行拓展。这样的好处是显而易见的,一是增加了框架的灵活性。二是开发者可以结合实际需求,对框架进行拓展,使其能够更好的工作。以MyBatis为例,我们可基于MyBatis插件机制实现分页、分表,监控等功能。由于插件和业务无关,业务也无法感知插件的存在。因此可以无感植入插件,在无形中增强功能

二:Mybatis插件介绍

Mybatis作为一个应用广泛的优秀的ORM开源框架,这个框架具有强大的灵活性,在四大组件 (Executor、StatementHandler、ParameterHandler、ResultSetHandler)处提供了简单易用的插件扩展机制。Mybatis对持久层的操作就是借助于四大核心对象。MyBatis支持用插件对四大核心对象进行拦截,对mybatis来说插件就是拦截器,用来增强核心对象的功能,增强功能本质上是借助于底层的 动态代理实现的,换句话说,MyBatis中的四大对象都是代理对象

MyBatis所允许拦截的方法如下:

  • 执行器Executor (update、query、commit、rollback等方法);
  • SQL语法构建器StatementHandler (prepare、parameterize、batch、updates query等方法);
  • 参数处理器ParameterHandler (getParameterObject、setParameters方法);
  • 结果集处理器ResultSetHandler (handleResultSets、handleOutputParameters等方法);

三:Mybatis插件原理

在四大对象创建的时候

  • 1、每个创建出来的对象不是直接返回的,而interceptorChain.pluginAll(parameterHandler);
  • 2、获取到所有的Interceptor (拦截器)(插件需要实现的接口);调用 interceptor.plugin(target);返回 target 包装后的对象
  • 3、插件机制,我们可以使用插件为目标对象创建一个代理对象;AOP (面向切面)我们的插件可以为四大对象创建出代理对象,代理对象就可以拦截到四大对象的每一个执行; 拦截

插件具体是如何拦截并附加额外的功能的呢?以ParameterHandler来说

public ParameterHandler newParameterHandler(MappedStatement mappedStatement,Object object, BoundSql sql, InterceptorChain interceptorChain){
    ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement,object,sql);
    parameterHandler = (ParameterHandler)
    interceptorChain.pluginAll(parameterHandler);
    return parameterHandler;
}
public Object pluginAll(Object target) {
    for (Interceptor interceptor : interceptors) 
    {
        target = interceptor.plugin(target);
    }
    return target;
}

interceptorChain保存了所有的拦截器(interceptors),是mybatis初始化的时候创建的。调用拦截器链中的拦截器依次的对目标进行拦截或增强。interceptor.plugin(target)中的target就可以理解为mybatis 中的四大对象。返回的target是被重重代理后的对象

如果我们想要拦截Executor的query方法,那么可以这样定义插件:

@Intercepts({
    @Signature(
        type = Executor.class,
        method = "query",
        args={MappedStatement.class,Object.class,RowBounds.class,ResultHandler.class}
        )
})
public class ExeunplePlugin implements Interceptor {
//省略逻辑 
}

除此之外,我们还需将插件配置到sqlMapConfig.xml中。

<plugins>
    <plugin interceptor="com.lagou.plugin.ExamplePlugin"></plugin>
</plugins>

这样MyBatis在启动时可以加载插件,并保存插件实例到相关对象(InterceptorChain,拦截器链) 中。待准备工作做完后,MyBatis处于就绪状态。我们在执行SQL时,需要先通过DefaultSqlSessionFactory 创 建 SqlSession。Executor 实例会在创建 SqlSession 的过程中被创建, Executor实例创建完毕后, MyBatis会通过JDK动态代理为实例生成代理类。这样,插件逻辑即可在 Executor相关方法被调用前执行。

以上就是MyBatis插件机制的基本原理.

希望对各位小伙伴有所帮助