Mybatis源码 插件机制

简介

插件是一种常见的扩展方式,大多数开源框架也都支持用户通过添加自定义插件的方式来扩展或者改变原有的功能,MyBatis中也提供的有插件,虽然叫插件,但是实际上是通过拦截器(Interceptor)实现的,在MyBatis的插件模块中涉及到责任链模式和JDK动态代理。

如何自定义实现插件?

step1:实现Interceptor接口;

step2:配置拦截器在全局配置文件

<plugins><plugin interceptor="com.dura.interceptor.FirstInterceptor"><-- 这里的这个属性,我没写哈;不过,这里的用法倒是挺重要的,定义在拦截器中属性  --><property name="testProp" value="1000"/></plugin></plugins>

step3:运行。

插件实现的原理?

初始化操作——全局配置文件解析

该方法用来解析全局配置文件中的plugins标签,然后对应的创建Interceptor对象,并且封装对应的属性信息。最后调用了Configuration对象中的方法。 configuration.addInterceptor(interceptorInstance)

通过这个代码我们发现我们自定义的拦截器最终是保存在了InterceptorChain这个对象中。而InterceptorChain的定义为

创建代理对象过程?

在解析的时候创建了对应的Interceptor对象,并保存在了InterceptorChain中,那么这个拦截器是如何和对应的目标对象进行关联的呢

首先拦截器可以拦截的对象是Executor,ParameterHandler,ResultSetHandler,StatementHandler。

那么我们来看下这四个对象在创建的时候又什么要注意的

Executor

Executor在装饰完二级缓存后会通过pluginAll来创建Executor的代理对象。

StatementHandler

  @Overridepublic <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {Statement stmt = null;try {Configuration configuration = ms.getConfiguration();// 注意,已经来到SQL处理的关键对象 StatementHandlerStatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);// 获取一个 Statement对象stmt = prepareStatement(handler, ms.getStatementLog());// 执行查询return handler.query(stmt, resultHandler);} finally {// 用完就关闭closeStatement(stmt);}}// 进入创建的方法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;}//  可以看到statementHandler的代理对象

ParameterHandler

  public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {// StatementType 是怎么来的? 增删改查标签中的 statementType="PREPARED",默认值 PREPAREDswitch (ms.getStatementType()) {case STATEMENT:delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);break;case PREPARED:// 创建 StatementHandler 的时候做了什么? >>delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);break;case CALLABLE:delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);break;default:throw new ExecutorException("Unknown statement type: " + ms.getStatementType());}}// 进入某个Handler看下  
public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql);// 植入插件逻辑(返回代理对象)parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);return parameterHandler;}

ResultSetHandler

  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;}

执行过程

以Executor的query方法为例,当查询请求到来的时候,Executor的代理对象是如何处理拦截请求的呢?我们来看下。当请求到了executor.query方法的时候


// 执行Plugin的invoke 方法
/*** 代理对象方法被调用时执行的代码* @param proxy* @param method* @param args* @return* @throws Throwable*/@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {try {// 获取当前方法所在类或接口中,可被当前Interceptor拦截的方法Set<Method> methods = signatureMap.get(method.getDeclaringClass());if (methods != null && methods.contains(method)) {// 当前调用的方法需要被拦截 执行拦截操作return interceptor.intercept(new Invocation(target, method, args));}// 不需要拦截 则调用 目标对象中的方法return method.invoke(target, args);} catch (Exception e) {throw ExceptionUtil.unwrapThrowable(e);}}//然后进入interceptor.intercept 会进入我们自定义的 FirstInterceptor对象中/*** 执行拦截逻辑的方法* @param invocation* @return* @throws Throwable*/@Overridepublic Object intercept(Invocation invocation) throws Throwable {System.out.println("FirtInterceptor  拦截之前 ....");Object obj = invocation.proceed(); // 执行目标方法System.out.println("FirtInterceptor  拦截之后 ....");return obj;}

以上就是自定义的拦截器执行的完整流程。

如果我们有多个拦截器怎么办

如果我们有多个自定义的拦截器,那么他的执行流程是怎么样的呢?比如我们创建了两个 Interceptor 都是用来拦截 Executor 的query方法,一个是用来执行逻辑A 一个是用来执行逻辑B的。

总结:Interceptor的相关对象作用

对象作用
Interceptor自定义插件需要实现接口,实现4个方法
InterceptChain配置的插件解析后会保存在Configuration的InterceptChain中
Plugin触发管理类,还可以用来创建代理对象
Invocation对被代理类进行包装,可以调用proceed()调用到被拦截的方法

应用场景分析——其实在说拦截器啦

作用描述实现方式
水平分表一张费用表按月度拆分为12张表。fee_202001-202012。当查询条件出现月度(tran_month)时,把select语句中的逻辑表名修改为对应的月份表。对query update方法进行拦截在接口上添加注解,通过反射获取接口注解,根据注解上配置的参数进行分表,修改原SQL,例如id取模,按月分表
数据脱敏手机号和身份证在数据库完整存储。但是返回给用户,屏蔽手机号的中间四位。屏蔽身份证号中的出生日期。query——对结果集脱敏
菜单权限控制不同的用户登录,查询菜单权限表时获得不同的结果,在前端展示不同的菜单对query方法进行拦截在方法上添加注解,根据权限配置,以及用户登录信息,在SQL上加上权限过滤条件
黑白名单有些SQL语句在生产环境中是不允许执行的,比如like %%对Executor的update和query方法进行拦截,将拦截的SQL语句和黑白名单 进行比较,控制SQL语句的执行
全局唯一ID在高并发的环境下传统的生成ID的方式不太适用,这时我们就需要考虑其他方式了创建插件拦截Executor的insert方法,通过UUID或者雪花算法来生成ID,并修改SQL中的插入信息

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/899470.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Android14 SystemUI中添加第三方AIDL

由于特殊需求&#xff0c;需要在SystemUI中添加第三方AIDL&#xff0c;去做一些客制化的修改。现在记录一下AIDL添加的过程。 1.将AIDL文件拷贝到frameworks/base/packages/SystemUI/src/下&#xff0c;我要添加的AIDL文件是com/test/myctr/IDevicectr.aidl&#xff0c;添加后的…

Binlog、Redo log、Undo log的区别

一、binlog和redo log的区别 特性binlogredo log记录对象记录的是 MySQL 服务器的事务操作&#xff0c;针对的是整个数据库实例。记录的是 InnoDB 存储引擎的数据页变化&#xff0c;针对的是具体的存储引擎层面。记录内容记录的是事务的逻辑操作&#xff0c;例如 SQL 语句&…

TDengine 中的异常恢复

简介 本章主要介绍在 TDengine 执行命令过程中发生异常&#xff0c;如何手工终于执行的任务。可以终止连接&#xff0c;线上查询及终止事务。 如果一个事务 在一个复杂的应用场景中&#xff0c;连接和查询任务等有可能进入一种错误状态或者耗时过长迟迟无法结束&#xff0c;…

全球化2.0 | ZStack举办香港Partner Day,推动AIOS智塔+DeepSeek海外实践

2025年3月21日&#xff0c;云轴科技ZStack在香港成功举办了主题为“ZStack AIOS 智塔与 DeepSeek 私有化方案介绍及企业应用落地实践”的 Partner Day 活动。此次活动吸引了众多海外合作伙伴&#xff0c;共同探讨 AI Infra 平台在企业私有化 AI 中的应用与价值闭环。 ZStack CT…

ERP、MES和CRM三大企业系统的详细介绍及对比分析

以下是关于ERP、MES和CRM三大企业系统的详细介绍及对比分析&#xff1a; 1. ERP&#xff08;企业资源计划&#xff0c;Enterprise Resource Planning&#xff09; 核心功能&#xff1a; 集成管理&#xff1a;财务、采购、库存、生产、人力资源等核心业务流程资源优化&#xf…

(二十)Dart 中的多态

Dart 中的多态教程 一、多态的概念 多态是面向对象编程中的一个重要概念。它允许将子类类型的指针赋值给父类类型的指针&#xff0c;同一个函数调用会有不同的执行效果。换句话说&#xff0c;子类的实例可以赋值给父类的引用。多态的核心在于父类定义一个方法不去实现&#x…

【C++初阶】第12课—list

文章目录 1. list的构造2. list迭代器的常见接口2.1 list遍历的迭代器接口2.2 list修改数据的迭代器接口2.3 list排序、逆序、合并相关操作的成员函数 3. 模拟实现list3.1 模拟实现list的构造3.2 模拟实现list的尾插3.3 模拟实现迭代器iterator3.4 模拟实现list的插入删除3.5 模…

思维链技术(Chain-of-Thought, CoT)

思维链&#xff08;Chain-of-Thought, CoT&#xff09;是一种通过模拟人类逐步推理过程来提升大型语言模型&#xff08;LLM&#xff09;复杂任务表现的技术。其核心思想是让模型在生成最终答案前&#xff0c;先输出中间推理步骤&#xff0c;从而增强逻辑性和可解释性。 1. 基础…

谷粒微服务高级篇学习笔记整理---异步线程池

多线程回顾 多线程实现的4种方式 1. 继承 Thread 类 通过继承 Thread 类并重写 run() 方法实现多线程。 public class MyThread extends Thread {Overridepublic void run() {System.out.println("线程运行: " Thread.currentThread().getName());} }// 使用 pub…

Windows学习笔记(4)关于MITRE

基本术语 APT&#xff08;威胁组&#xff0c;高级持续威胁&#xff09; TTP&#xff08;攻击目的技术过程&#xff0c;战术技术和程序&#xff09; ATT&CK框架 网站 https://attack.mitre.org/ CAR知识库 MITRE Engage MITRE D3FEND 网址 https://d3fend.mitre.org/

Go 语言规范学习(2)

文章目录 VariablesTypesBoolean typesNumeric typesString typesArray typesSlice typesStruct typesPointer typesFunction typesInterface typesBasic interfacesEmbedded interfacesGeneral interfaces【泛型接口】Implementing an interface【实现一个接口】 Map typesCha…

创意 Python 爱心代码分享

创意 Python 爱心代码分享 在编程中&#xff0c;用代码表达创意和情感是一种非常有趣的方式。本文将分享几段用 Python 编写的爱心代码&#xff0c;涵盖简单到复杂的实现方式&#xff0c;适合初学者和进阶开发者。 1. 简单爱心图案 代码实现 print("\n".join([&qu…

NLP高频面试题(二十四)——RAG相关内容简介

检索增强生成&#xff08;Retrieval-Augmented Generation&#xff0c;简称 RAG&#xff09;是一种将信息检索与生成模型相结合的技术&#xff0c;旨在提升大型语言模型的响应准确性、相关性和时效性。通过在生成过程中引入外部知识&#xff0c;RAG 能够有效弥补 LLM 在知识局限…

Share01-WinCC文件越用越大?

为什么你们的经典WinCC项目在客户电脑上运行的越来越慢&#xff1f;为什么查询一个历史曲线慢的要死&#xff1f;为什么重启一下电脑画面都要怀疑人生&#xff1f;具体原因可能多种多样&#xff0c;但是极大可能是您的数据管理设置欠佳&#xff0c;那么闲话少叙&#xff0c;和小…

练习题:111

目录 Python题目 题目 题目分析 需求理解 关键知识点 实现思路分析 代码实现 代码解释 指定文件路径和名称&#xff1a; 定义要写入的内容&#xff1a; 打开文件并写入内容&#xff1a; 异常处理&#xff1a; 输出提示信息&#xff1a; 运行思路 结束语 Python题…

2025_0327_生活记录

昨晚正在玩手机&#xff0c;凌晨一点二十一分左右手机突然响起来&#xff0c;通知地震波将在5秒后到达海淀区。看着倒计时的数字不断减小&#xff0c;橙色预警页面不断闪动&#xff0c;床猛地摇了几下。那一刻&#xff0c;我的记忆被拉回了2008年。 上大学之前我在成都生活了1…

基于改进粒子群算法的多目标分布式电源选址定容规划(附带Matlab代码)

通过分析分布式电源对配电网的影响&#xff0c;以有功功率损耗、电压质量及分布式电源总容量为优化目标&#xff0c;基于模糊理论建立了分布式电源在配电网中选址定容的多目标优化模型&#xff0c;并提出了一种改进粒子群算法进行求解。在算例仿真中&#xff0c;基于IEEE-14标准…

雨云云应用测评!内测持续进行中!

大家好&#xff0c;时隔一个月&#xff0c;我们又见面了&#xff01; 最近&#xff0c;雨云推出了新型云应用&#xff08;RCA&#xff0c;Rainyun Cloud Application&#xff09;。 通过云应用&#xff0c;你可以快速创建可以外部访问的应用&#xff0c;采用全新的面板和dock…

【研究方向】联邦|自然语言

联邦学习 Federated Learning,FL 分布式学习方案。 通过多个参与方&#xff08;client&#xff09; 联邦计算 Federated Computing 联邦计算(Federated Learning)是一种分布式 机器学习 方法,旨在解决数据隐私保护与数据孤岛问题。 图联邦 Graph Neural Networks,GNNs 图联…

【算法day25】 最长有效括号——给你一个只包含 ‘(‘ 和 ‘)‘ 的字符串,找出最长有效(格式正确且连续)括号子串的长度。

32. 最长有效括号 给你一个只包含 ‘(’ 和 ‘)’ 的字符串&#xff0c;找出最长有效&#xff08;格式正确且连续&#xff09;括号子串的长度。 https://leetcode.cn/problems/longest-valid-parentheses/ 2.方法二&#xff1a;栈 class Solution { public:int longestValid…