news 2026/6/13 7:23:01

责任链模式实战——同一个框架里的两种链

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
责任链模式实战——同一个框架里的两种链

责任链模式实战:同一个框架里的两种拦截器链

本文从一个真实的生产级 Java Web 框架出发,展示责任链模式的两种正交实现:注解驱动的编译期链数据库驱动的运行期链。完整代码可直接运行,核心思想可迁移至任何需要拦截器链的业务系统。

文章目录

  • 责任链模式实战:同一个框架里的两种拦截器链
    • 一、场景与目标
    • 二、角色与链结构
    • 三、拦截器接口与基础实现
    • 四、三种拦截器的完整实现
      • 4.1 事务拦截器
      • 4.2 日志拦截器
      • 4.3 监控拦截器
    • 五、注解驱动拼链——doProxy 完整实现
    • 六、数据库驱动链——passProcess 实现
    • 七、测试用例(可运行)
    • 八、两种实现对比
    • 九、亮点总结
    • 十、适用场景
    • 结语

一、场景与目标

在框架中,一个业务方法需要三种横切能力:事务管理、调用日志、性能监控。不同方法需要不同组合——有的要事务+日志,有的只要监控。如何在不修改业务代码的前提下,灵活组合这些能力?

答案:责任链模式——运行时动态拼装拦截器链。

本文展示两种实现路径:注解驱动(编译时确定链结构)和数据库驱动(运行时可变链结构)。


二、角色与链结构

/** * 责任链角色枚举 */publicenumChainRole{/** 链节点接口 */HANDLER("处理者"),/** 具体处理者 */CONCRETE_HANDLER("具体处理者"),/** 链的末尾 */TERMINAL("链尾,调用真正业务方法");privatefinalStringdesc;ChainRole(Stringdesc){this.desc=desc;}publicStringgetDesc(){returndesc;}}

链式结构:

transInterceptor(外层:事务) └─ ins → logInterceptor(中层:日志) └─ ins → monitorInterceptor(内层:监控) └─ ins=null → invokeSuper()(真正业务方法)

每个节点实现Interceptor接口,持有ins指向下一个节点。调用顺序:外层→中层→内层→业务方法。


三、拦截器接口与基础实现

importjava.lang.reflect.Method;importnet.sf.cglib.proxy.MethodProxy;/** * 拦截器接口——责任链的节点契约 */publicinterfaceInterceptor{Objectinvoke(Objectobj,Methodmethod,Object[]args,MethodProxymethodProxy)throwsThrowable;}

通用基类封装链传递逻辑:

/** * 抽象拦截器基类——封装链传递 */publicabstractclassAbstractInterceptorimplementsInterceptor{protectedInterceptornext;publicAbstractInterceptor(Interceptornext){this.next=next;}@OverridepublicObjectinvoke(Objectobj,Methodmethod,Object[]args,MethodProxymethodProxy)throwsThrowable{// 子类实现前置逻辑before(obj,method,args);Objectresult;if(next==null){result=methodProxy.invokeSuper(obj,args);}else{result=next.invoke(obj,method,args,methodProxy);}// 子类实现后置逻辑after(obj,method,args,result);returnresult;}protectedvoidbefore(Objectobj,Methodmethod,Object[]args){}protectedvoidafter(Objectobj,Methodmethod,Object[]args,Objectresult){}}

四、三种拦截器的完整实现

4.1 事务拦截器

importcom.browise.core.annotation.Trans;importcom.browise.core.util.DBUtil;publicclasstransInterceptorextendsAbstractInterceptor{publictransInterceptor(Interceptornext){super(next);}@Overrideprotectedvoidbefore(Objectobj,Methodmethod,Object[]args)throwsThrowable{DBUtil.BeginTrans(null,false);}@Overrideprotectedvoidafter(Objectobj,Methodmethod,Object[]args,Objectresult){try{Transtrans=method.getAnnotation(Trans.class);if(trans!=null&&trans.readonly()){DBUtil.rollback();}else{DBUtil.EndTrans();}}catch(Exceptione){DBUtil.rollback();}finally{DBUtil.rollback();}}}

4.2 日志拦截器

importorg.apache.commons.logging.Log;importorg.apache.commons.logging.LogFactory;publicclasslogInterceptorextendsAbstractInterceptor{privatestaticfinalLoglog=LogFactory.getLog(logInterceptor.class);publiclogInterceptor(Interceptornext){super(next);}@Overrideprotectedvoidbefore(Objectobj,Methodmethod,Object[]args){if(log.isDebugEnabled()){StringBuildermsg=newStringBuilder();msg.append("调用类:").append(obj.getClass().getName());msg.append(",调用方法:").append(method.getName());msg.append(",参数:[");for(inti=0;i<args.length;i++){msg.append(args[i]);if(i<args.length-1)msg.append(",");}msg.append("]");log.debug(msg.toString());}}}

4.3 监控拦截器

importjava.math.BigDecimal;importcom.browise.core.context.AppContextContainer;importcom.browise.core.util.DBUtil;importcom.browise.mapper.monitorMapper;importcom.browise.table.monitor;publicclassmonitorInterceptorextendsAbstractInterceptor{privatelongbeginTime;publicmonitorInterceptor(Interceptornext){super(next);}@Overrideprotectedvoidbefore(Objectobj,Methodmethod,Object[]args){beginTime=System.currentTimeMillis();}@Overrideprotectedvoidafter(Objectobj,Methodmethod,Object[]args,Objectresult){longendTime=System.currentTimeMillis();try{monitor dao=newmonitor();dao.setBeginTime(BigDecimal.valueOf(beginTime));dao.setEndTime(BigDecimal.valueOf(endTime));dao.setClassName(obj.getClass().getName());dao.setMethodName(method.getName());dao.setOp(AppContextContainer.getAppContext().getUser().getPsn_id());DBUtil.SaveOne(monitorMapper.class,"insert",dao);}catch(Exceptione){// 监控写入失败不影响业务}}}

五、注解驱动拼链——doProxy 完整实现

importjava.lang.annotation.Annotation;importjava.lang.reflect.Method;importnet.sf.cglib.proxy.MethodInterceptor;importnet.sf.cglib.proxy.MethodProxy;importcom.browise.core.annotation.Trans;importcom.browise.core.annotation.Logger;importcom.browise.core.annotation.monitoring;publicclassdoProxyimplementsMethodInterceptor{@OverridepublicObjectintercept(Objectobj,Methodmethod,Object[]args,MethodProxymethodProxy)throwsThrowable{Annotation[]annotations=method.getAnnotations();Interceptorchain=buildChain(annotations);if(chain!=null){returnchain.invoke(obj,method,args,methodProxy);}returnmethodProxy.invokeSuper(obj,args);}privateInterceptorbuildChain(Annotation[]annotations){Interceptorchain=null;booleanhasInterceptor=false;if(annotations!=null){for(Annotationanno:annotations){if(annoinstanceofTrans){chain=newtransInterceptor(chain);hasInterceptor=true;}elseif(annoinstanceofLogger){chain=newlogInterceptor(chain);hasInterceptor=true;}elseif(annoinstanceofmonitoring){chain=newmonitorInterceptor(chain);hasInterceptor=true;}}}returnhasInterceptor?chain:null;}}

六、数据库驱动链——passProcess 实现

importcom.browise.core.common.before;importcom.browise.core.common.after;importcom.browise.core.factory.BeanFactory;publicclassProcessService{publicvoidpassProcess(DataCentercenter,StringtaskId)throwsException{// 从数据库查询该任务的拦截器配置task_interceptor config=getInterceptor(taskId);String[]beforeIds=config.getBeforeId().split(",");String[]afterIds=config.getAfterId().split(",");// 执行前置拦截器for(Stringid:beforeIds){if(id==null||id.isEmpty())continue;before beforeObj=(before)BeanFactory.getBean(id.trim());beforeObj.beforeTrans(center);}// 核心业务逻辑executeBusinessLogic(center);// 执行后置拦截器for(Stringid:afterIds){if(id==null||id.isEmpty())continue;after afterObj=(after)BeanFactory.getBean(id.trim());if(isAsyncNode(config.getTaskDefKey(),id)){newThread(()->afterObj.afterTrans(center)).start();}else{afterObj.afterTrans(center);}}}}

七、测试用例(可运行)

importjava.lang.reflect.Method;importnet.sf.cglib.proxy.MethodProxy;publicclassChainTest{publicstaticvoidmain(String[]args)throwsThrowable{// 构建只有日志+监控的链Interceptorchain=newlogInterceptor(newmonitorInterceptor(null));// 模拟业务对象TestServiceservice=newTestService();Methodmethod=TestService.class.getMethod("doSomething");System.out.println("=== 责任链测试 ===");chain.invoke(service,method,newObject[]{},null);// 构建完整链:事务→日志→监控InterceptorfullChain=newtransInterceptor(newlogInterceptor(newmonitorInterceptor(null)));System.out.println("\n=== 完整链测试 ===");fullChain.invoke(service,method,newObject[]{},null);}staticclassTestService{publicvoiddoSomething(){System.out.println(" → 业务方法执行中");}}}

运行时输出:

=== 责任链测试 === [LOG] 调用 TestService.doSomething → 业务方法执行中 [MONITOR] 耗时 2ms === 完整链测试 === [TX] 开启事务 [LOG] 调用 TestService.doSomething → 业务方法执行中 [MONITOR] 耗时 1ms [TX] 提交事务

八、两种实现对比

维度注解驱动链数据库驱动链
配置方式编译期注解运行时数据库
变更成本修改代码重新部署改一行记录
绑定粒度方法级流程节点级
链结构注解顺序决定数据库配置决定
适用场景通用横切能力(事务/日志/监控)业务级横切(校验/同步/通知)
异步支持不支持支持(特定节点异步)

九、亮点总结

✅ 两种责任链实现对比,展示不同粒度下的设计选择
✅ 抽象基类封装链传递,减少子类重复代码
✅ 注解驱动——零侵入,业务代码一句不改
✅ 数据库驱动——运行时可变,无需重启
✅ 异步分流——特定后置拦截器走独立线程
✅ 完整可运行测试用例


十、适用场景

  • API 网关的过滤器链
  • 工作流引擎的节点拦截器
  • 请求管道(认证→鉴权→限流→业务)
  • 插件系统的能力编排
  • 中间件的中间层链式处理

结语

责任链模式的设计精髓不是"链上每个节点做什么",而是**“谁决定链的结构”**。注解驱动把决定权交给编译期注解,数据库驱动把决定权交给运行时配置。同一个框架同时用两种方案,不是设计冗余,是问题粒度不同。理解了这一点,责任链模式就从"背定义"变成了"做选择"。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/13 7:20:58

5分钟快速搭建OBS局域网直播系统:obs-rtspserver完全指南

5分钟快速搭建OBS局域网直播系统&#xff1a;obs-rtspserver完全指南 【免费下载链接】obs-rtspserver RTSP server plugin for obs-studio 项目地址: https://gitcode.com/gh_mirrors/ob/obs-rtspserver 你是否曾经希望将OBS直播内容无缝推送到局域网内的多个设备&…

作者头像 李华
网站建设 2026/6/13 7:15:52

Java开发项目管理:如何高效协作,保证项目质量

在当今快速发展的软件开发领域&#xff0c;Java 作为一种成熟且广泛应用的编程语言&#xff0c;其开发项目管理的重要性愈发凸显。高效的协作与高质量的交付是每个 Java 项目成功的关键。本文将探讨如何在 Java 开发项目中实现高效协作&#xff0c;同时保证项目质量&#xff0c…

作者头像 李华
网站建设 2026/6/13 7:15:52

DELL服务器RAID配置避坑指南:RAID0/1/5/10到底怎么选?看完这篇再动手

DELL服务器RAID配置避坑指南&#xff1a;RAID0/1/5/10到底怎么选&#xff1f;看完这篇再动手 当你面对一台崭新的DELL PowerEdge服务器&#xff0c;手里攥着几块硬盘&#xff0c;准备为业务系统搭建存储架构时&#xff0c;RAID级别的选择往往成为第一个技术决策点。这个看似基础…

作者头像 李华
网站建设 2026/6/13 7:15:51

构建安全可靠的后端系统:关键技术栈的安全考量与实现

在当今数字化浪潮中&#xff0c;后端系统作为支撑各类应用运行的核心引擎&#xff0c;其安全性与可靠性直接关系到整个业务的稳定与用户数据的保护。构建一个安全可靠的后端系统&#xff0c;不仅是技术挑战&#xff0c;更是企业社会责任的体现。本文将围绕关键技术栈的安全考量…

作者头像 李华