本文已参与「新人创作礼」活动,一起开启掘金创作之路。
一、Mybatis 的插件机制
1.1 插件简介
Mybatis 通过四大核心组件,对开发者友好提供了易于扩展的插件机制。对 Mybatis 来说插件就是拦截器,用来增强核心对象的功能,增强功能本质上是借助于底层的动态代理实现的。
- Executor:执行器。处理
update/query/commit/rollback等方法 - StatementHandler:SQL 语法构建器。处理
prepare/parameterize/batch/update/query等方法 - ParameterHandler:参数处理器。处理
getParameterObject/setParameters等方法 - ResultSetHandler:结果集处理器。处理
handleResultSets/handleOutputParameters等方法
1.2 插件使用
-
实现接口
Interceptor/** * 文件描述 * * @Project yiwenup-sample * @Package cloud.yiwenup.sample.mybatis.plugin * @Author yiwenup * @Date 2022-06-10 10:46:01 * @Description */ @Intercepts({ // 此处需要配置待拦截的组件信息,method 的方法名称以及 args 参数信息都需要准确,可以参考 StatementHandler 源码配置 @Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class}) }) public class MyPlugin implements Interceptor { /** * 每次执行到待拦截方法的时候,都会进入增强方法 */ @Override public Object intercept(Invocation invocation) throws Throwable { System.out.println("@@@@@@@@@@@@@@@@@@@增强逻辑..."); return invocation.proceed(); } /** * 为拦截器生成一个代理,放入拦截器链中 */ @Override public Object plugin(Object target) { System.out.println("@@@@@@@@@@@@@@@@@@@将要包装的目标对象:" + target); return Plugin.wrap(target, this); } /** * 插件初始化的时候会被首先调用,仅调用一次,方便将外部配置引入 */ @Override public void setProperties(Properties properties) { System.out.println("@@@@@@@@@@@@@@@@@@@插件配置的初始化参数:" + properties); } } -
全局配置文件中注册拦截器
<plugins> <plugin interceptor="cloud.yiwenup.sample.mybatis.plugin.MyPlugin"> <property name="username" value="zhangsan"/> </plugin> </plugins> -
测试结果
1.3 插件原理
- 插件核心的位置是实现
intercept方法,在此处可以定制增强内容,另外的关键点是plugin方法,这个方法中的默认实现是调用了Plugin.wrap(target, intercept) Plugin.wrap(target, intercept)的底层静态实现中,首先会校验插件MyPlugin的类注解,看看是不是声明了@Intercepts/@Signature,组装待拦截信息为一个Map<Class<?>, Set<Method>>,其中 Map 的 key 便是四大组件之一,Map 的 value 是其中的方法- 获取到
Map<Class<?>, Set<Method>>之后,Plugin.wrap(target, intercept)拿出target参数的类信息,判断当前待代理的对象是否是在Map<Class<?>, Set<Method>>中,如果不在,则直接放行 - 如果
target是在待拦截对象中,那么就使用JDK动态代理为其生成代理对象,最终执行到InvocationHandler#invoke中 - 在
Plugin#invoke中,首先还是判断当前的方式是否是Map<Class<?>, Set<Method>>中的待增强方法,如果是,则执行MyPlugin#intercept,否则进行放行处理
1.4 插件机制的使用场景
- PageHelper:分页插件
- tk.mybatis:基于 Mybatis 封装的增强通用 Mapper
二、Mybatis 架构原理
2.1 Mybatis 主要构件及相互关系
- SqlSession:作为MyBatis工作的主要顶层API,表示和数据库交互的会话,完成必要数据库增删改查功能
- Executor:MyBatis执行器,是MyBatis调度的核心,负责SQL语句的生成和查询缓存的维护
- StatementHandler:封装了 JDBC Statement 操作,负责对 JDBC statement 的操作,如设置参数、将 Statement 结果集转换成 List集合
- ParameterHandler:负责对用户传递的参数转换成 JDBC Statement 所需要的参数。主要是参数映射配置、参数映射解析、参数类型解析
- ResultSetHandler:负责将 JDBC 返回的 ResultSet 结果集对象转换成 List 类型的集合
- TypeHandler:负责 java 数据类型和 jdbc 数据类型之间的映射和转换
- MappedStatement:维护了一条 <select | update | delete | insert> 节点的封装
- SqlSource:负责根据用户传递的 parameterObject,动态地生成 SQL 语句,将信息封装到 BoundSql 对象中,并返回
- BoundSql:表示动态生成的 SQL 语句以及相应的参数信息
2.2 流程解析
- 加载配置文件,或者解析注解参数,封装
Configuration对象 - 使用端调用上层 API,通过
SqlSession开启会话发送数据请求 - 框架层接收到请求,根据
statementId查询MappedStatement - 根据
MappedStatement对象获取最终要执行的 SQL 和传入的参数 - 获取数据库连接,访问数据库,执行 SQL 并拿到执行结果
- 根据
MappedStatement对象中的结果集映射配置对得到的执行结果进行转换,最终得到处理结果 - 释放资源并返回处理结果
三、Mybatis 周边
- PageHelper
- Mybatis-Plus
- tk.mybatis