Java设计模式之责任链模式详细讲解和案例示范
在本文中,我们将详细讲解Java设计模式中的责任链模式,探讨其基本概念、使用场景、常见问题和解决方式。同时,我们还会介绍责任链模式与策略模式的区别,并结合电商交易系统的示例进行说明。此外,我们还会探讨责任链模式在开源框架中的应用。
1. 责任链模式概述
责任链模式是一种行为设计模式,旨在将请求沿着链传递,直到被某个处理器处理。它使得多个对象都有机会处理请求,避免了请求发送者与接收者的耦合。
1.1 主要特点
- 请求传递:请求沿着链条依次传递,直到找到能够处理该请求的处理器。
- 灵活性:可以动态增加或删除处理器,调整处理链的顺序。
- 解耦:请求发送者和处理者之间没有直接的依赖关系。
1.2 结构
责任链模式主要包括以下几个角色:
- Handler:定义了处理请求的接口,并包含了对下一个处理器的引用。
- ConcreteHandler:具体的处理器类,负责处理具体的请求。
- Client:发出请求的客户端,依赖于Handler接口。
2. 使用场景
- 请求需要多个对象进行处理:例如,在订单处理系统中,不同的订单状态可能由不同的处理器进行处理。
- 动态的请求处理链:如审批流程,处理链可以根据业务需求进行动态配置。
3. 电商交易系统中的示例
以电商交易系统为例,我们将演示如何使用责任链模式来处理订单状态的变化。例如,订单可能会经历“已支付”、“已发货”、“已收货”等状态,每个状态的处理都可以看作是责任链中的一个环节。
3.1 错误示范
在没有使用责任链模式的情况下,订单状态的处理可能会写成一长串的if-else语句,这样的代码可读性差,维护困难。
public class OrderService {public void processOrder(Order order) {if (order.getStatus().equals("PAID")) {// 处理已支付的订单} else if (order.getStatus().equals("SHIPPED")) {// 处理已发货的订单} else if (order.getStatus().equals("DELIVERED")) {// 处理已收货的订单}// 其他状态处理}
}
3.2 正确示范
使用责任链模式可以将不同状态的处理分离到各自的处理器中,增强代码的灵活性和可维护性。
abstract class OrderHandler {protected OrderHandler nextHandler;public void setNextHandler(OrderHandler nextHandler) {this.nextHandler = nextHandler;}public abstract void handleOrder(Order order);
}class PaidOrderHandler extends OrderHandler {@Overridepublic void handleOrder(Order order) {if (order.getStatus().equals("PAID")) {// 处理已支付的订单} else if (nextHandler != null) {nextHandler.handleOrder(order);}}
}class ShippedOrderHandler extends OrderHandler {@Overridepublic void handleOrder(Order order) {if (order.getStatus().equals("SHIPPED")) {// 处理已发货的订单} else if (nextHandler != null) {nextHandler.handleOrder(order);}}
}class DeliveredOrderHandler extends OrderHandler {@Overridepublic void handleOrder(Order order) {if (order.getStatus().equals("DELIVERED")) {// 处理已收货的订单} else if (nextHandler != null) {nextHandler.handleOrder(order);}}
}
使用责任链模式后,我们可以灵活地添加、删除或调整处理器顺序,无需修改现有代码。
3.3 类图
4. 常见问题与解决方式
4.1 问题一:责任链过长导致性能问题
在某些场景下,责任链可能会变得过长,导致每次请求传递的效率低下。
解决方式:
- 优化链条顺序:将最常用的处理器放在链条前面。
- 分拆责任链:将责任链拆分成多个子链,分别处理不同的请求。
4.2 问题二:处理器无法处理请求
当所有处理器都无法处理请求时,可能会导致请求被忽略。
解决方式:
- 添加默认处理器:在链的末尾添加一个默认的处理器,处理无法被处理的请求。
class DefaultOrderHandler extends OrderHandler {@Overridepublic void handleOrder(Order order) {// 处理无法处理的请求}
}
5. 责任链模式与策略模式的区别
责任链模式与策略模式都是行为型模式,但它们的使用场景和目的有所不同。
- 责任链模式:关注请求的传递,通过一系列的处理器逐个处理请求。适用于多个对象依次处理请求的场景。
- 策略模式:关注算法的替换,通过定义一组算法类来实现相同的功能。适用于需要动态切换算法的场景。
6. 责任链模式在开源框架中的应用
责任链模式在许多开源框架中得到了广泛应用,例如Spring Security中的过滤器链就是责任链模式的典型应用。
6.1 Spring Security过滤器链简介
Spring Security是一个为Java企业应用提供安全功能的框架。它的核心之一就是过滤器链,用来对请求进行认证和授权。每个请求在进入应用程序之前,都会经过一组过滤器。每个过滤器依次处理请求,完成特定的安全检查任务,如身份验证、权限检查、跨站点请求伪造(CSRF)防护等。
这些过滤器形成了一个“责任链”,请求在链上一个一个过滤器中传递,直到某个过滤器处理完毕或整个链处理结束。
Spring Security的过滤器链是基于Servlet API中的javax.servlet.Filter
接口构建的。该接口的核心方法是doFilter()
,它接受请求和响应作为参数,并负责将请求传递给链中的下一个过滤器。
6.2 责任链模式的应用结构
在责任链模式中,通常会有以下几个角色:
- 处理者(Handler): 定义一个处理请求的接口。每个具体的处理者实现该接口来处理部分请求。
- 具体处理者(Concrete Handler): 处理链中实际的处理器,处理请求或将请求传递给下一个处理者。
- 客户(Client): 发起请求的对象,不关心谁处理了请求,也不需要知道具体的处理者。
在Spring Security中,处理者相当于Filter
,而具体处理者是各种具体的安全过滤器,如UsernamePasswordAuthenticationFilter
、BasicAuthenticationFilter
等。客户是HTTP请求。
6.3 Spring Security责任链模式工作流程
当客户端请求到达Spring Security的过滤器链时,过滤器链按照顺序执行每一个过滤器,直到某个过滤器决定阻止请求继续处理或最后一个过滤器处理完请求。
流程大致如下:
- 客户端发起请求:请求首先被交给第一个过滤器处理。
- 过滤器链依次执行:每个过滤器执行其
doFilter()
方法,执行安全检查。 - 请求传递:如果当前过滤器无法处理请求,或者处理完毕后仍需进一步检查,则将请求传递给下一个过滤器。
- 终止或继续:若某个过滤器决定终止请求(例如认证失败),则直接返回响应,不再执行后续过滤器;否则,继续传递请求。
- 最后一个过滤器完成处理:所有过滤器执行完毕后,若没有过滤器拦截请求,则请求到达目标资源。
6.4 案例:自定义过滤器链
现在我们通过一个实际的案例,演示如何在Spring Security中使用自定义过滤器链。假设我们需要在现有的Spring Security过滤器链中增加一个自定义的认证过滤器,用来处理特殊的请求认证逻辑。
步骤1:创建自定义过滤器 我们首先创建一个继承自OncePerRequestFilter
的过滤器,并在其中实现自定义的认证逻辑。
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.filter.OncePerRequestFilter;import java.io.IOException;public class CustomAuthenticationFilter extends OncePerRequestFilter {@Overrideprotected void doFilterInternal(HttpServletRequest request,HttpServletResponse response,FilterChain filterChain)throws ServletException, IOException {String token = request.getHeader("Authorization");// 简单的认证逻辑:如果存在token,就通过认证if (token != null && token.startsWith("Bearer ")) {// 模拟将用户信息存入SecurityContextSecurityContextHolder.getContext().setAuthentication(new CustomAuthenticationToken(token));}// 继续传递请求到下一个过滤器filterChain.doFilter(request, response);}
}
在这个过滤器中,我们从HTTP请求的头部获取Authorization
信息,并检查它是否以Bearer
开头。如果符合条件,就将认证信息存储在SecurityContextHolder
中。之后,调用filterChain.doFilter()
将请求继续传递给下一个过滤器。
步骤2:配置自定义过滤器链
接下来,我们需要将自定义的过滤器加入到Spring Security的过滤器链中。通过扩展WebSecurityConfigurerAdapter
并重写configure
方法,我们可以将自定义过滤器插入到合适的位置。
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {@Overrideprotected void configure(HttpSecurity http) throws Exception {http.addFilterBefore(new CustomAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class).authorizeRequests().anyRequest().authenticated();}
}
在此配置中,我们使用了addFilterBefore()
方法,将自定义的CustomAuthenticationFilter
添加到UsernamePasswordAuthenticationFilter
之前。这样,自定义过滤器将会在用户名密码认证过滤器之前执行。
步骤3:测试过滤器链
至此,过滤器链已经配置完成。我们可以通过HTTP请求来测试我们的过滤器链是否正常工作。
curl -H "Authorization: Bearer abc123" http://localhost:8080/api/orders
当客户端发送带有Authorization
头的请求时,自定义过滤器会处理认证逻辑,随后将请求继续传递给下一个过滤器。如果认证通过,Spring Security将允许访问资源,否则将会返回未认证错误。
通过这种设计,Spring Security可以灵活地配置过滤器的顺序和内容,增强系统的可扩展性。
7. 总结
责任链模式在处理复杂的请求传递场景中非常有效,尤其是在需要灵活调整处理顺序的系统中。通过本篇文章的介绍,你应该已经掌握了责任链模式的基本概念、使用场景、常见问题以及如何在实际项目中应用它
相关文章:

Java设计模式之责任链模式详细讲解和案例示范
在本文中,我们将详细讲解Java设计模式中的责任链模式,探讨其基本概念、使用场景、常见问题和解决方式。同时,我们还会介绍责任链模式与策略模式的区别,并结合电商交易系统的示例进行说明。此外,我们还会探讨责任链模式…...
ubuntu_如何解决apt install时报错:Waiting for cache lock: Could not get lock
当你在 Ubuntu 上运行 apt 时,遇到类似 Waiting for cache lock: Could not get lock 错误,通常是因为另一个进程正在使用 apt 或者类似的包管理器工具。你可以按照以下步骤来查找并解决这个问题: 1. 查询哪个进程正在使用锁 系统中的锁文件…...

软件测试(D5)
步骤: 设计测试-->发现缺陷-->测试报告 Day1 target 1.复述软件测试的定义 2.7种软件测试分类的区别 3.质量模型的重点5项 4.测试流程的6个步骤 5.测试模板的8个要素 认识软件及测试 软件: 控制硬件的工具 应用软件系统软件࿰…...

CSS 圆角渐变边框
<div class"contact-box"><div class"contact-item">联系我们</div> </div>.contact-item{width: 194px;height: 48px;border-radius: 20px 20px 20px 20px;background-color: #000000;color: #BDBDBD;font-weight: 500;font-size…...
骑砍2霸主MOD开发(26)-使用TrfExporterBlender制作TRF文件
一.Blender导入TRF文件 import bpytrf_meshes = []trf_contents = []trf_import_path = D:\pt_ladder.trftrf_export_path = D:\pt_ladder_morph_keys.trfclass TrfMesh:def __init__(self):self.mesh_name = self.mesh_materials = []self.vertex_cnt = 0self.vertex_fvf_cnt…...

Leetcode 最大子数组和
使用“Kadane’s Algorithm”来解决。 Kadane’s Algorithm 在每个步骤中都保持着一个局部最优解,即以当前元素为结尾的最大子数组和(也就是局部最优解),并通过比较这些局部最优解和当前的全局最优解来找到最终的全局最优解。 Kadane’s Algorithm的核…...
目标检测-YOLOv2
YOLOv2介绍 YOLOv2(You Only Look Once version 2)是一种用于目标检测的深度学习模型,由Joseph Redmon等人于2016年提出,并详细论述在其论文《YOLO9000: Better, Faster, Stronger》中。YOLOv2在保持高速检测的同时,显…...

大数据 - OLAP与OLTP的区别
前言 联机事务处理OLTP(on-line transaction processing)和 联机分析处理OLAP(On-Line Analytical Processing)。 OLTP,主要是面向传统的“增删改查”事务系统,数据大都是以实体对象模型来存储数据&#…...

win10+eclipse+ESP8266_RTOS_SDK开发环境构建
官网教程 https://docs.espressif.com/projects/esp8266-rtos-sdk/en/latest/get-started/eclipse-setup.html 1. 导入工程 Build and Flash with Eclipse IDE — ESP8266 RTOS SDK Programming Guide documentation (espressif.com) 导入整个SDK,便于查看所有代…...

树形弹窗选择框/vue2/Element/弹框选择
前言 此类选择器根据vueelementUI实现,使用vue3的可以根据此案例稍作改动即可实现,主要功能有弹出选择、搜索过滤、搜索结果高亮等,此选择器只支持单选,如需多选可在此基础进行改造。 效果图 代码实现 使用时,props-…...
Python精选200Tips:121-125
Spend your time on self-improvement 121 Requests - 简化的 HTTP 请求处理发送 GET 请求发送 POST 请求发送 PUT 请求发送 DELETE 请求会话管理处理超时文件上传122 Beautiful Soup - 网页解析和抓取解析 HTML 和 XML 文档查找单个标签查找多个标签使用 CSS 选择器查找标签提…...

对接后端download接口报未知异常错误
你一定遇到过这种情况,在一个项目中下载功能明明好好的,下载接口调用方法与前端调用方法封装的好好的,可是换了一个接口,竟然搞罢工了,类似下面这样的,你会不会无从下手,不知道该怎么办呢&#…...

vue3 指定元素全屏 screenfull(可直接粘贴使用)
业务需求 由于输入的文字较多,需要将输入框进行全屏展示,方便输入和查看! 效果图 实现方式 下载插件"screenfull": “^6.0.2” yarn add screenfull -S项目中使用 import screenfull from "screenfull"templte中代码…...
【规范】Git Commit 约定式提交规范
文章目录 前言介绍使用约定式提交规范的好处提交信息格式信息头部(Header)正文(Body)脚注(Footer)撤销(Revert) 提交类型表格官网 前言介绍 约定式提交规范它是一种基于提交信息的轻…...
GDB的基本使用方法(之一)
1.编译程序 如果要让GDB调试程序,则编译生成程序时,要添加-g编译选项: $gcc -Wall -O2 -g 源文件 编译器含有针对源代码中的各种各样的错误输出信息的功能,称为警告选项。这些信息并不一定是错误,但却指出了容易引发bug的编码方式。-Werror选项可以在警告发生时,将其当…...

DoubletFinder去除双细胞分析学习
在单细胞RNA测序过程中,有时两个或多个细胞可能在制备过程中意外结合成一个单一的"假细胞",称为双峰细胞或双倍体。这些双峰细胞可能会扭曲数据分析和解释,因此,需要使用一些方法对它们进行识别和剔除。其中DoubletFind…...
软考高级第四版备考---第四十八天(项目基本要素-项目项目、项目集、项目组合和运营管理之间的关系)
一、概述: 项目集是一组相互关联且被协调管理的项目、子项目集和项目集活动,目的是为了获得分别管理无法获得的利益。项目集不是大项目,大项目是指规模、影响等特别大的项目; 项目组合是指为实现战略目标而组合在一起管理的项目、…...
系统架构设计师:信息系统基础知识
简简单单 Online zuozuo: 简简单单 Online zuozuo 简简单单 Online zuozuo 简简单单 Online zuozuo 简简单单 Online zuozuo :本心、输入输出、结果 简简单单 Online zuozuo : 文章目录 系统架构设计师:信息系统基础知识前言信息系统构成:信息系统功能:信息系统生命周期…...

微服务-nacos
nacos-注册中心 启动 服务注册到nacos...

快速上手 | 数据可观测性平台 Datavines 自定义SQL规则使用指南
摘要 本文主要介绍在 Datavines平台已有规则不能满足需求的情况下,如何通过自定义SQL规则来实现基于业务特性的数据质量检查。 规则介绍 自定义聚合SQL规则是 Datavines 平台中内置的一个灵活的规则,该规则允许用户通过编写SQL的方式来实现想要的数据质…...

相机Camera日志实例分析之二:相机Camx【专业模式开启直方图拍照】单帧流程日志详解
【关注我,后续持续新增专题博文,谢谢!!!】 上一篇我们讲了: 这一篇我们开始讲: 目录 一、场景操作步骤 二、日志基础关键字分级如下 三、场景日志如下: 一、场景操作步骤 操作步…...
mongodb源码分析session执行handleRequest命令find过程
mongo/transport/service_state_machine.cpp已经分析startSession创建ASIOSession过程,并且验证connection是否超过限制ASIOSession和connection是循环接受客户端命令,把数据流转换成Message,状态转变流程是:State::Created 》 St…...
Python爬虫实战:研究feedparser库相关技术
1. 引言 1.1 研究背景与意义 在当今信息爆炸的时代,互联网上存在着海量的信息资源。RSS(Really Simple Syndication)作为一种标准化的信息聚合技术,被广泛用于网站内容的发布和订阅。通过 RSS,用户可以方便地获取网站更新的内容,而无需频繁访问各个网站。 然而,互联网…...

1.3 VSCode安装与环境配置
进入网址Visual Studio Code - Code Editing. Redefined下载.deb文件,然后打开终端,进入下载文件夹,键入命令 sudo dpkg -i code_1.100.3-1748872405_amd64.deb 在终端键入命令code即启动vscode 需要安装插件列表 1.Chinese简化 2.ros …...
【服务器压力测试】本地PC电脑作为服务器运行时出现卡顿和资源紧张(Windows/Linux)
要让本地PC电脑作为服务器运行时出现卡顿和资源紧张的情况,可以通过以下几种方式模拟或触发: 1. 增加CPU负载 运行大量计算密集型任务,例如: 使用多线程循环执行复杂计算(如数学运算、加密解密等)。运行图…...

听写流程自动化实践,轻量级教育辅助
随着智能教育工具的发展,越来越多的传统学习方式正在被数字化、自动化所优化。听写作为语文、英语等学科中重要的基础训练形式,也迎来了更高效的解决方案。 这是一款轻量但功能强大的听写辅助工具。它是基于本地词库与可选在线语音引擎构建,…...

JVM 内存结构 详解
内存结构 运行时数据区: Java虚拟机在运行Java程序过程中管理的内存区域。 程序计数器: 线程私有,程序控制流的指示器,分支、循环、跳转、异常处理、线程恢复等基础功能都依赖这个计数器完成。 每个线程都有一个程序计数…...

热烈祝贺埃文科技正式加入可信数据空间发展联盟
2025年4月29日,在福州举办的第八届数字中国建设峰会“可信数据空间分论坛”上,可信数据空间发展联盟正式宣告成立。国家数据局党组书记、局长刘烈宏出席并致辞,强调该联盟是推进全国一体化数据市场建设的关键抓手。 郑州埃文科技有限公司&am…...
flow_controllers
关键点: 流控制器类型: 同步(Sync):发布操作会阻塞,直到数据被确认发送。异步(Async):发布操作非阻塞,数据发送由后台线程处理。纯同步(PureSync…...

leetcode_69.x的平方根
题目如下 : 看到题 ,我们最原始的想法就是暴力解决: for(long long i 0;i<INT_MAX;i){if(i*ix){return i;}else if((i*i>x)&&((i-1)*(i-1)<x)){return i-1;}}我们直接开始遍历,我们是整数的平方根,所以我们分两…...