东莞金融网站建设,深圳 电子商务网站开发,集团网站设计专业团队,网站开发设计培训目录
一、核心角色对应#xff08;责任链模式#xff09;
二、MyBatis 责任链的核心拦截点
三、责任链执行流程#xff08;以 StatementHandler 为例#xff09;
1. 整体流程
2. 关键环节#xff1a;嵌套代理构建责任链
四、代码示例#xff1a;自定义插件实现责任…目录一、核心角色对应责任链模式二、MyBatis 责任链的核心拦截点三、责任链执行流程以 StatementHandler 为例1. 整体流程2. 关键环节嵌套代理构建责任链四、代码示例自定义插件实现责任链步骤 1定义抽象处理者复用 MyBatis 原生 Interceptor步骤 2实现具体处理者自定义插件插件 1SQL 日志插件记录最终执行的 SQL插件 2分页插件改写 SQL 拼接分页条件步骤 3配置插件构建责任链步骤 4执行效果责任链执行流程五、责任链核心实现Plugin 类管理器关键逻辑六、MyBatis 责任链的核心特点1. 嵌套代理式责任链区别于列表式2. 不可中断的纯责任链3. 精准拦截按方法签名匹配4. 无侵入扩展七、典型应用场景八、使用注意事项总结MyBatis 基于责任链模式 JDK 动态代理实现了插件Interceptor机制核心目标是对 SQL 执行全流程参数处理、SQL 构建、结果集返回等进行无侵入的拦截增强多个插件可按顺序形成责任链依次处理目标对象最终执行原生逻辑。与 Spring MVC/Security 的 “列表式责任链” 不同MyBatis 责任链是嵌套代理式每个插件通过动态代理包裹目标对象请求沿代理链传递本质仍是 “请求沿链传递、处理者自主决策” 的责任链核心思想。一、核心角色对应责任链模式责任链模式角色MyBatis 对应实现核心职责抽象处理者Handlerorg.apache.ibatis.plugin.Interceptor接口定义拦截核心方法intercept、代理创建方法plugin是所有插件的统一接口具体处理者自定义插件如分页插件 PageHelper、SQL 日志插件、MyBatis 内置插件实现Interceptor指定拦截点如StatementHandler的prepare方法处理拦截逻辑后传递请求责任链管理器org.apache.ibatis.plugin.Plugin类基于 JDK 动态代理构建插件链触发拦截方法执行控制请求传递被处理的 “请求” 对象MyBatis 核心执行组件Executor/StatementHandler/ParameterHandler/ResultSetHandler插件拦截的目标也是请求传递的载体如修改StatementHandler的 SQL 内容最终处理者MyBatis 原生组件的方法如StatementHandler.prepare()责任链执行完毕后执行原生 SQL 处理逻辑二、MyBatis 责任链的核心拦截点MyBatis 插件可拦截 4 个核心组件的方法即责任链可处理的 “请求类型”覆盖 SQL 执行全流程拦截组件拦截时机典型用途ExecutorSQL 执行前 / 后query/update分页、缓存增强、SQL 耗时统计StatementHandlerSQL 构建 / 执行前prepare/parameterizeSQL 改写、动态拼接条件ParameterHandlerSQL 参数绑定前setParameters参数加密、参数格式化ResultSetHandler结果集处理后handleResultSets结果集脱敏、数据类型转换三、责任链执行流程以StatementHandler为例1. 整体流程plaintextMyBatis 执行 SQL → 创建 StatementHandler 对象 → Plugin 类为其生成代理对象嵌套所有插件→ 调用 StatementHandler.prepare() → 触发代理链执行 插件1.intercept() → 处理逻辑如改写 SQL→ 调用 invocation.proceed() → 插件2.intercept() → 处理逻辑如记录 SQL→ 调用 invocation.proceed() → ... → 执行原生 StatementHandler.prepare() → 执行 SQL 并返回结果2. 关键环节嵌套代理构建责任链MyBatis 插件链通过Plugin.wrap()方法嵌套构建例如配置了 “分页插件 日志插件”plaintext原始 StatementHandler → 被日志插件代理LogPlugin→ 被分页插件代理PagePlugin→ 最终代理对象调用代理对象的prepare()时执行顺序为PagePlugin.intercept()→LogPlugin.intercept()→ 原始prepare()即配置顺序 代理外层顺序 拦截执行顺序。四、代码示例自定义插件实现责任链步骤 1定义抽象处理者复用 MyBatis 原生 InterceptorMyBatis 已提供抽象处理者接口无需自定义java运行public interface Interceptor { // 核心拦截方法处理逻辑通过 invocation.proceed() 传递请求 Object intercept(Invocation invocation) throws Throwable; // 创建代理对象将当前插件加入责任链 default Object plugin(Object target) { return Plugin.wrap(target, this); } // 设置插件属性从 mybatis-config.xml 读取 default void setProperties(Properties properties) {} }步骤 2实现具体处理者自定义插件插件 1SQL 日志插件记录最终执行的 SQLjava运行Intercepts({ // 指定拦截点拦截 StatementHandler 的 prepare 方法 Signature( type StatementHandler.class, method prepare, args {Connection.class, Integer.class} ) }) public class SqlLogInterceptor implements Interceptor { Override public Object intercept(Invocation invocation) throws Throwable { // 1. 获取目标对象StatementHandler StatementHandler statementHandler (StatementHandler) invocation.getTarget(); // 2. 反射获取 SQL 内容MyBatis 提供的反射工具 MetaObject metaObject SystemMetaObject.forObject(statementHandler); String sql (String) metaObject.getValue(delegate.boundSql.sql); // 3. 处理逻辑打印 SQL System.out.println(【SQL 日志插件】执行 SQL sql); // 4. 传递请求执行下一个插件/原生方法 return invocation.proceed(); } }插件 2分页插件改写 SQL 拼接分页条件java运行Intercepts({ Signature( type StatementHandler.class, method prepare, args {Connection.class, Integer.class} ) }) public class PageInterceptor implements Interceptor { private int pageSize; // 每页条数 Override public Object intercept(Invocation invocation) throws Throwable { // 1. 获取目标对象 StatementHandler statementHandler (StatementHandler) invocation.getTarget(); MetaObject metaObject SystemMetaObject.forObject(statementHandler); // 2. 处理逻辑改写 SQL拼接 LIMIT String originalSql (String) metaObject.getValue(delegate.boundSql.sql); String pageSql originalSql LIMIT pageSize; metaObject.setValue(delegate.boundSql.sql, pageSql); System.out.println(【分页插件】改写 SQL pageSql); // 3. 传递请求 return invocation.proceed(); } Override public void setProperties(Properties properties) { // 读取配置的分页大小 pageSize Integer.parseInt(properties.getProperty(pageSize, 10)); } }步骤 3配置插件构建责任链在mybatis-config.xml中注册插件配置顺序即责任链执行顺序xmlconfiguration plugins !-- 第一个注册分页插件外层代理 -- plugin interceptorcom.example.PageInterceptor property namepageSize value5/ /plugin !-- 第二个注册日志插件内层代理 -- plugin interceptorcom.example.SqlLogInterceptor/ /plugins /configuration步骤 4执行效果责任链执行流程当执行userMapper.selectAll()时输出如下plaintext【分页插件】改写 SQLSELECT * FROM user LIMIT 5 【SQL 日志插件】执行 SQLSELECT * FROM user LIMIT 5执行顺序分页插件先拦截修改 SQL→ 日志插件后拦截打印最终 SQL→ 执行原生prepare()方法核心传递逻辑每个插件通过invocation.proceed()触发下一个插件 / 原生方法执行实现 “请求沿链传递”。五、责任链核心实现Plugin 类管理器MyBatis 的Plugin类是责任链的核心基于 JDK 动态代理实现拦截和传递核心源码简化如下java运行public class Plugin implements InvocationHandler { private final Object target; // 被代理的目标对象如 StatementHandler private final Interceptor interceptor; // 当前插件 private final MapClass?, SetMethod signatureMap; // 插件要拦截的方法 // 创建代理对象嵌套构建责任链 public static Object wrap(Object target, Interceptor interceptor) { // 获取插件要拦截的方法映射 MapClass?, SetMethod signatureMap getSignatureMap(interceptor); Class? type target.getClass(); // 获取目标类的所有接口JDK 动态代理需要接口 Class?[] interfaces getAllInterfaces(type, signatureMap); if (interfaces.length 0) { // 生成代理对象当前 Plugin 作为 InvocationHandler return Proxy.newProxyInstance( type.getClassLoader(), interfaces, new Plugin(target, interceptor, signatureMap) ); } return target; // 无需拦截返回原对象 } // 代理对象调用方法时触发核心责任链执行 Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 判断当前方法是否是插件要拦截的方法 if (signatureMap.containsKey(method.getDeclaringClass())) { if (signatureMap.get(method.getDeclaringClass()).contains(method)) { // 执行插件的 intercept 方法传入 Invocation封装目标、方法、参数 return interceptor.intercept(new Invocation(target, method, args)); } } // 不拦截直接执行目标方法传递请求 return method.invoke(target, args); } }关键逻辑wrap()方法为目标对象生成代理若有多个插件会嵌套生成代理如Plugin.wrap(Plugin.wrap(target, pagePlugin), logPlugin)invoke()方法代理对象调用方法时先执行插件的intercept方法再通过Invocation.proceed()触发下一层代理 / 原生方法。六、MyBatis 责任链的核心特点1. 嵌套代理式责任链区别于列表式传统责任链如 Spring MVC用列表存储处理者循环执行MyBatis 责任链用动态代理嵌套处理者请求沿代理链传递更轻量且适配 “拦截方法执行” 的场景。2. 不可中断的纯责任链MyBatis 插件必须通过invocation.proceed()传递请求否则原生逻辑无法执行无法像 Spring MVC 那样 “中断链”属于纯责任链请求必须传递到最终处理者。3. 精准拦截按方法签名匹配通过InterceptsSignature注解指定拦截的 “组件 方法 参数”仅拦截匹配的方法避免无效处理。4. 无侵入扩展新增插件只需实现Interceptor并配置无需修改 MyBatis 源码符合 “开闭原则”。七、典型应用场景分页插件PageHelper拦截Executor的query方法或StatementHandler的prepare方法拼接分页 SQL如LIMIT/ROWNUMSQL 监控 / 日志拦截StatementHandler的prepare方法记录 SQL 内容、执行耗时、参数信息数据脱敏拦截ParameterHandler入参脱敏如手机号隐藏中间 4 位、ResultSetHandler出参脱敏多租户适配拦截StatementHandler自动为 SQL 拼接租户 ID 条件如WHERE tenant_id ?参数加密 / 解密拦截ParameterHandler对敏感参数如密码加密拦截ResultSetHandler对结果解密。八、使用注意事项插件顺序配置顺序决定执行顺序外层插件先执行需注意依赖如分页插件应在日志插件前执行否则日志打印原 SQL反射安全修改 MyBatis 内部属性如 SQL时必须使用MetaObjectMyBatis 原生反射工具避免直接反射导致版本兼容问题性能控制插件会增加 SQL 执行耗时避免过多 / 复杂插件如同时拦截Executor和StatementHandler方法匹配Signature的args必须与目标方法参数完全一致如StatementHandler.prepare()的参数是Connection和Integer需精准匹配。总结MyBatis 是责任链模式在 “ORM 框架拦截 SQL 执行” 场景的经典落地核心载体Interceptor是抽象处理者Plugin是责任链管理器通过动态代理嵌套构建插件链执行逻辑请求沿代理链传递每个插件处理后通过invocation.proceed()传递给下一个插件最终执行原生逻辑核心价值无侵入扩展 SQL 执行流程适配分页、日志、脱敏等多样化业务需求关键扩展自定义插件只需实现Interceptor配置后即可加入责任链是 MyBatis 灵活性的核心体现。掌握这一机制是开发 MyBatis 定制化插件如自定义分页、SQL 改写的核心前提。