插件简介
一般情况下,开源框架都会提供插件或者扩展点,供开发者自行拓展。这样的好处显而易见,一是增加了框架的灵活性,二是开发者结合实际需求,对框架进行扩展,使它能够更好的工作。
以 MyBatis 为例子,我们可以基于 MyBatis 插件机制实现分页、分表、监控功能。由于插件和业务无关,业务也无法感知插件的存在,因此可以无感植入插件,在无形中实现增强。
MyBatis 插件机制允许用户通过实现插件接口来扩展 MyBatis 的功能,拦截 SQL 语句的执行过程,进行自定义的处理。这是 MyBatis 提供的一种灵活的机制,可以在不修改核心代码的情况下对其行为进行定制。插件可以用于日志记录、性能监控、SQL 统计等多个场景。
基本概念
MyBatis 插件机制基于 Java 的 拦截器模式,通过插件类实现对 MyBatis 核心行为的拦截和修改。插件可以在执行特定的操作时(如查询、更新、删除等)插入自定义代码。常见的插件场景包括:
- 拦截执行 SQL 的过程:可以修改、增强或记录 SQL 执行的细节。
- 增加额外的处理逻辑:比如日志、缓存、性能监控等。
作用范围
插件的作用对象是 MyBatis 内部的核心组件,如 Executor、StatementHandler、ResultSetHandler 和 ParameterHandler。你可以选择拦截这些组件的具体操作,例如:
- Executor:执行 SQL 的操作。
- StatementHandler:处理 SQL 语句的生成、参数设置等。
- ResultSetHandler:处理结果集的映射。
- ParameterHandler:处理 SQL 语句的参数设置。
插件通过 intercept 方法拦截目标对象,决定是否执行后续操作或修改返回值。
常见场景
- SQL 日志记录: 记录执行的 SQL 语句和执行时间,有助于进行性能监控和问题排查。
- SQL 性能监控: 插件可以记录 SQL 执行的时间,帮助开发者识别性能瓶颈,优化 SQL 执行。
- 事务控制: 插件可以用来在执行 SQL 前后添加自定义的事务控制逻辑,如重试机制等。
- 缓存: 插件可以用于实现二级缓存或其他缓存机制,避免重复查询相同数据。
- SQL 改写: 插件可以修改 SQL 语句,例如添加某些条件、修改排序等,扩展 MyBatis 的查询功能。
优点
- 灵活性:插件机制允许开发者在不修改 MyBatis 核心代码的情况下,灵活地增加或修改功能。
- 扩展性:插件可以轻松地扩展 MyBatis,满足各种业务需求。
- 可定制性:可以根据业务需求定制插件,实现特定的功能,如日志记录、性能监控等。
缺点
- 调试复杂:由于插件是通过代理模式实现的,调试时可能会增加复杂性。
- 性能开销:每次 SQL 执行时都需要经过插件的拦截和处理,可能会引入一定的性能开销。
插件介绍
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:handlerResultSets handleOutputParameters 等方法
插件原理
在四大核心对象创建的时候:
- 每个创建出来都不是直接返回的,而是 interceptorChain.pluginAll(parameterHandler)
- 获取到所有 interceptor,调用 interceptor.plugin(target)返回 target 包装后的对象
- 插件机制,可以使用插件为目标对象创建一个代理对象,AOP(面向切面)我们的插件可以为四大对象创建出代理对象,代理对象可以拦截到四大对象的每一个执行。
插件具体是如何拦截并附加额外的功能呢?比如 ParameterHandler:
interceptorChain 保存了所有的拦截器(interceptors),是 MyBatis 初始化的时候创建的,调用拦截器链中的拦截器依次对目标进行拦截或增强。interceptor.plugin(target)中的 target 就可以理解为 MyBatis 中的四大对象,返回的 target 就是被多重代理后的对象。
如果我们想要拦截 Executor 的 query 方法,那么可以定义:
除此之外,我们还需要将插件配置到 sqlMapConfig.xml 上:
这样 MyBatis 在启动时可以加载插件,并保存插件实例相关对象(InterceptorChain,拦截器)。待准备工作结束后,MyBatis 对于就绪状态,我们在执行 SQL 时,需要先通过 DefaultSqlSessionFactory 创建 SQL Session。Executor 实例会在创建 SqlSession 的过程中被创建,Executor 实例创建完毕后,MyBatis 会通过 JDK 动态代理为实例生成代理类,这样,插件逻辑可以在 Executor 相关方法开始调用前执行。
自定义插件
MyBatis 插件接口 interceptor:
- interceptor 方法,插件的核心方法
- plugin 方法,生成 target 的代理对象
- setProperties 方法,传递插件所需参数
源码分析
插件执行逻辑,Plugin 实现了 InvocationHandler 接口,因此它的 Invoke 方法会拦截所有的方法调用,invoke 方法会对所有拦截的方法进行检测,以决定是否执行插件逻辑,该方法的逻辑如下:
对应的截图如下所示:

可以看到,invoke 方法的代码比较少,逻辑不难理解,首先,invoke方法会检测被拦截方法,插件逻辑封装在 interceptor 中,该方法的参数类型为 Invocation 主要用于存储目标类,方法以及方法参数列表,下面简单看一下该类的定义:
对应的截图如下所示:

7129

被折叠的 条评论
为什么被折叠?



