写在前面,该篇博文为我在部门的技术分享,分享时长约40分钟。以至于文章中更多的只有源代码截图,更多细节为现场口述。本文更像是一个大纲,通过6+3个真实的责任链代码实现,为我们能够灵活运用责任链设计模式添砖加瓦。希望对你有帮助!

浅谈责任链设计模式在框架源码中的运用

  • ​​一、分享目的​​
  • ​​二、简单介绍​​
  • ​​三、逐个拆解​​
  • ​​四、源码环节​​
  • ​​1、Tomcat​​
  • ​​2、Spring MVC​​
  • ​​3、Spring AOP​​
  • ​​4、MyBatis​​
  • ​​5、HSF​​
  • ​​6、Netty​​
  • ​​五、简单实战​​
  • ​​1、日终跑批​​
  • ​​2、结算​​
  • ​​3、接收外部数据(http)​​
  • ​​六、最终总结​​

一、分享目的

  • 认识责任链设计模式
  • 看得懂责任链设计模式不同的写法
  • 如果有机会,可以灵活运用在项目中


二、简单介绍

责任链模式(Chain of Responsibility Pattern)为请求创建了一个接收者对象的链。这种模式给予请求的类型,对请求的发送者和接收者进行解耦。这种类型的设计模式属于行为型模式(A->B这个过程,拆分为若干段,分别写在不同的类)



浅谈责任链设计模式在框架源码中的运用_拦截器

主体的对象:抽象处理者(xxxContext、xxxChain)包含众多具体处理者(xxxHandler、xxxFilter)


三、逐个拆解

初印象

  1. 数据来源(链的数据哪来的?)
  • 项目启动阶段加载
  1. 构建形式(这个链是什么形状?)
  • 普通list、或者链表
  1. 拦截角度(这个链是什么形状?)
  • 直接拦截
  1. 关联细节(执行流程是怎么样?)
  • 依次完成逻辑
  1. 影响面(功能涉及影响面多少?)
  • 可大可小


四、源码环节

接下来我将通过一个具体的业务场景,逐步向大家讲述框架作者在以上五个维度中是如何体现的。看看责任链能怎么写,顺便提一下源码设计中的小细节。

项目背景:在一个使用SpringBoot构建的Web项目中,持久层使用MyBatis,Web容器使用Tomcat,RPC框架使用Dubbo/HSF

案例背景:用户前台查看id=1的数据,后台将id传给第三方服务进行查询,然后返回数据给前台,当前项目操作日志需要落库。



1、Tomcat

请求首先来到Tomcat,当前为HTTP请求,先经过传输层逻辑,再跳转到对应的应用层解析类中,最后doGet–>processRequest–>doService–>doDispatch



浅谈责任链设计模式在框架源码中的运用_开发语言_02


浅谈责任链设计模式在框架源码中的运用_数据_03


浅谈责任链设计模式在框架源码中的运用_设计模式_04

与之类似的,Tomcat中还有一个org.apache.catalina.Valve接口



浅谈责任链设计模式在框架源码中的运用_java_05

补充:

一个是tomcat基于Servlet规范出来的Filter,一个是Tomcat内部自己私有的Filter。我们的多数据源过滤就是使用Servlet的Filter



2、Spring MVC

此时来到DispatcherServlet,该类会完成根据映射路径转发请求,最后进入controller层。下图代码为在转发逻辑前后的拦截器代码

构建形式和Tomcat中Filter形式大体相同



浅谈责任链设计模式在框架源码中的运用_拦截器_06

小细节。拦截器执行方法afterCompletion方法执行顺序。(applyPreHandle和afterCompletion更像是一对)



浅谈责任链设计模式在框架源码中的运用_设计模式_07


3、Spring AOP

Controller调用Service层方法,如果该方法被@AutoLog注解修饰,可以完成打日志的功能,该对象的构建使用到了AOP。我们都知道AOP是动态代理,这是一个笼统的概念,具体到细节的执行层面,其实现具体代理逻辑流程则是使用责任链构建相应的执行流程。



浅谈责任链设计模式在框架源码中的运用_拦截器_08


浅谈责任链设计模式在框架源码中的运用_拦截器_09


浅谈责任链设计模式在框架源码中的运用_设计模式_10


4、MyBatis

调用Mapper接口完成操作日志落库。如果使用MyBatis做为持久层框架,会在预处理我们的SQL前(prepareStatement),根据配置文件里配置的拦截器,完成相关的拦截器功能



浅谈责任链设计模式在框架源码中的运用_拦截器_11


浅谈责任链设计模式在框架源码中的运用_数据_12

MyBatisPlus的乐观锁、Pagehelp、对某些特定表的操作记录操作日志等功能



5、HSF

进入Service层后,业务逻辑RPC调用其他服务接口(在此之前,被调用方接口需要将自己的接口元数据信息序列化后放入三方注册中心),调用方接口根据指定的名字去注册中心找数据。这个元数据里面就会封装相关的拦截器属性,用于调用或者被调用的时候执行。

内部框架,略,细节类比Dubbo。



6、Netty

元数据序列化完成,使用Netty将数据传输到注册中心,并且可以接受对应的数据



浅谈责任链设计模式在框架源码中的运用_设计模式_13


浅谈责任链设计模式在框架源码中的运用_开发语言_14


浅谈责任链设计模式在框架源码中的运用_设计模式_15


五、简单实战

1、日终跑批

  1. 刷新授信
  2. 日终批量记账
  3. 生成日终分录
  4. 核算余额对账
  5. 切日


2、结算

  1. 授信处理
  2. 核心记账
  3. 生成分录
  4. 生成日间分录
  5. 发送支付


3、接收外部数据(http)

  1. 处理请求
  2. 保存日志
  3. 封装对象
  4. 业务处理
  5. 返回


六、最终总结

  1. 数据来源多:spi直接加载(HSF)、借助Spring加载(业务代码)、根据业务代码选择加载(Spring MVC)、数据库或配置文件
  2. 构建结构多:普通List(MyBatis)、单向链表(Tomcat ValveBase)、双向链表(HSF、Netty)、递归结构(Spring AOP)
  3. 拦截角度多:单纯走流程(MyBatis)、可以中途直接返回(Tomcat)、返回了但又没有完全返回(Spring MVC)
  4. 关联细节多:每一步相互独立(Tomcat Filter)、每一步相互关联(Netty)
  5. 框架影响多:少(MyBatis)、多(Netty、Zuul、Gateway)


小思考:我们看到责任链的呈现形式可以是递归调用的,那么请问我使用装饰者模式不停的向上装饰自己(MyBatis的Cache),或者说我使用代理模式不断代理之前的自己有什么区别?



浅谈责任链设计模式在框架源码中的运用_java_16


那我给出的答案就是:以结果为导向,大方向不跑偏,小方向看心情。