Java设计模式之责任链模式详细讲解和案例示范

news2024/9/21 23:34:40

在本文中,我们将详细讲解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 {
    @Override
    public void handleOrder(Order order) {
        if (order.getStatus().equals("PAID")) {
            // 处理已支付的订单
        } else if (nextHandler != null) {
            nextHandler.handleOrder(order);
        }
    }
}

class ShippedOrderHandler extends OrderHandler {
    @Override
    public void handleOrder(Order order) {
        if (order.getStatus().equals("SHIPPED")) {
            // 处理已发货的订单
        } else if (nextHandler != null) {
            nextHandler.handleOrder(order);
        }
    }
}

class DeliveredOrderHandler extends OrderHandler {
    @Override
    public 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 {
    @Override
    public 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,而具体处理者是各种具体的安全过滤器,如UsernamePasswordAuthenticationFilterBasicAuthenticationFilter等。客户是HTTP请求。

6.3 Spring Security责任链模式工作流程

当客户端请求到达Spring Security的过滤器链时,过滤器链按照顺序执行每一个过滤器,直到某个过滤器决定阻止请求继续处理或最后一个过滤器处理完请求。

流程大致如下:

  1. 客户端发起请求:请求首先被交给第一个过滤器处理。
  2. 过滤器链依次执行:每个过滤器执行其doFilter()方法,执行安全检查。
  3. 请求传递:如果当前过滤器无法处理请求,或者处理完毕后仍需进一步检查,则将请求传递给下一个过滤器。
  4. 终止或继续:若某个过滤器决定终止请求(例如认证失败),则直接返回响应,不再执行后续过滤器;否则,继续传递请求。
  5. 最后一个过滤器完成处理:所有过滤器执行完毕后,若没有过滤器拦截请求,则请求到达目标资源。
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 {

    @Override
    protected void doFilterInternal(HttpServletRequest request,
                                    HttpServletResponse response,
                                    FilterChain filterChain)
            throws ServletException, IOException {

        String token = request.getHeader("Authorization");

        // 简单的认证逻辑:如果存在token,就通过认证
        if (token != null && token.startsWith("Bearer ")) {
            // 模拟将用户信息存入SecurityContext
            SecurityContextHolder.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 {

    @Override
    protected 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. 总结

责任链模式在处理复杂的请求传递场景中非常有效,尤其是在需要灵活调整处理顺序的系统中。通过本篇文章的介绍,你应该已经掌握了责任链模式的基本概念、使用场景、常见问题以及如何在实际项目中应用它

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

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

相关文章

新手c语言讲解及题目分享(十九)--数据类型专项练习

本文主要讲解c语言的基础部分,常见的c语言基础数据类型,这个也非常重要。 参考书目和推荐学习书目:通过网盘分享的文件:C语言程序设计电子教材 (1).pdf 链接: https://pan.baidu.com/s/1JFqSaCKZ0A2Lr944e72NUA?pwdp648 提取码: …

Golang协程泄漏定位和排查

1 场景:无缓冲channel写阻塞 package mainimport ("log""net/http"_ "net/http/pprof""time""github.com/gin-gonic/gin" )func main() {go func() {log.Println(http.ListenAndServe("localhost:6060&quo…

uni-app实现web-view和App之间的相互通信

双向实时 如果app端部署成网站&#xff0c;则web-view就是iframe&#xff0c;使用也可以双向通讯 https://uniapp.dcloud.net.cn/component/web-view.html APP端代码 index.vue: <template><web-viewid"m-webview":fullscreen"true":src"…

数据结构-二叉树-基础知识

数据结构-二叉树-基础知识 1.树1.1什么是树1.2基本概念子节点、父节点叶节点节点的度树的高度/深度节点的子孙、祖先 1.3树与非树1.4如何实现1.5实例 2.二叉树2.1什么是二叉树2.2特殊的二叉树满二叉树完全二叉树 2.3性质层数度节点 2.4存储结构 1.树 1.1什么是树 树型结构是一…

C#笔记10 Thread类怎么终止(Abort)和阻止(Join)线程

Thread类 C#笔记8 线程是什么&#xff1f;多线程怎么实现和操作&#xff1f;-CSDN博客 C#笔记9 对线程Thread的万字解读 小小多线程直接拿下&#xff01;-CSDN博客 上次说过怎么简单的使用多线程&#xff0c;怎么创建多线程&#xff0c;但是没有具体分析它的终止和释放。 线…

【C++题解】1330. 求最大梯形的面积

欢迎关注本专栏《C从零基础到信奥赛入门级&#xff08;CSP-J&#xff09;》 问题&#xff1a;1330. 求最大梯形的面积 类型&#xff1a;二维数组 题目描述&#xff1a; 从键盘读入 n ( 3≤n≤100 )个梯形的上底、下底和高&#xff0c;请问这 n 个梯形中&#xff0c;最大面积…

3.ChatGPT在教育领域的应用:教学辅助与案例分享(3/10)

ChatGPT在教育领域的应用&#xff1a;教学辅助与案例分享 引言 在21世纪的教育领域&#xff0c;技术革新正以前所未有的速度改变着传统的教学和学习方式。随着人工智能&#xff08;AI&#xff09;的快速发展&#xff0c;教育技术&#xff08;EdTech&#xff09;领域迎来了新的…

“后期分块”:用长上下文嵌入模型拯救文本检索

论文&#xff1a;Late Chunking: Contextual Chunk Embeddings Using Long-Context Embedding Models地址&#xff1a;https://arxiv.org/abs/2409.04701 研究背景 研究问题&#xff1a;这篇文章要解决的问题是文本块嵌入在处理长文本时丢失上下文信息的问题。具体来说&#xf…

信刻光盘安全隔离与信息交换系统

随着各种数据传输、储存技术、信息技术的快速发展&#xff0c;保护信息安全是重中之重。军工、政府、部队及企事业单位等利用A网与B网开展相关工作已成为不可逆转的趋势。针对于业务需要与保密规范相关要求&#xff0c;涉及重要秘密信息&#xff0c;需做到安全的物理隔离&#…

队列的详细操作

1.循环队列--队列的顺序表示和实现 #include<stdio.h> #define MAXQSIZE 100 typedef struct {int* base;int front;int rear; }SqQueue;int InitQueue(SqQueue& Q) {Q.base new int[MAXQSIZE];//为队列分配一个最大容量为MAXSIZE的数组空间if (!Q.base)return -1;…

QXml 使用方法

VS2019 QT 编译工具链问题解决 使用winqtdeploy.exe 打包环境就可以正常运行&#xff0c;缺少某一个运行库引起的 简易使用python脚本编译运行 Python3 中的 slots 和 QT 中的 slots 宏定义重复, 放在不同的文件中进行调用可以避免 还是比较习惯从源码包引入&#xff08;方便定…

OpenAI新“草莓”模型即将发布,推理模式引发关注

OpenAI发布“草莓”模型的消息引发广泛关注。这个新模型将在未来两周内亮相&#xff0c;将为ChatGPT等产品带来全新的技术支持。草莓模型的推理模式具有独特性&#xff0c;能够仿效人类思维&#xff0c;在生成响应之前进行拟人化思考。其推理过程通常需要10到20秒&#xff0c;这…

2_foc闭环调试_ADC电流采样与滤波及pid数据结构

1、ADC电流采样 上次添加了编码器获取电角度的程序&#xff0c;将之前开环控制的角度进行了替换&#xff0c;这次再将电流采样添加进来&#xff0c;之后就可以利用这样一个有反馈的系统进行电流环PI控制器参数调试。 之前写过ADC&#xff0b;DMA电流采样的stm32库函数程序&…

《 C++ 修炼全景指南:八 》智能指针大揭秘:从 auto_ptr 到 unique_ptr shared_ptr 的进化之路

1、引言 在 C 编程中&#xff0c;内存管理历来是复杂且容易出错的部分。手动管理动态分配的内存不仅会导致内存泄漏&#xff0c;还会引发悬空指针和双重释放等问题。如何有效地管理动态内存&#xff0c;避免内存泄漏和未定义行为&#xff0c;往往是困扰初学者和资深开发者的难…

常见 HTTP 状态码详解与Nginx 文件上传大小限制

在我们日常使用 Nginx 搭建网站或应用服务时&#xff0c;可能会遇到很多与文件上传和请求响应相关的问题。今天我们就来聊聊 如何限制文件上传的大小&#xff0c;并介绍一些常见的 HTTP 状态码 及其在 Nginx 中的处理方式。 一、文件上传大小限制 有时&#xff0c;我们需要限…

Web3的崛起与智能合约的角色

随着Web3的发展&#xff0c;去中心化网络逐渐取代了以往的集中控制互联网模式。这一转变不仅强调了用户的自治权和隐私保护&#xff0c;还引入了智能合约这一核心技术。智能合约基于区块链技术&#xff0c;能够自动执行合约条款&#xff0c;无需中介干预&#xff0c;从而确保了…

git下载安装windows

https://git-scm.com/download/win 接下来傻瓜式安装就可以了

Django创建模型

1、根据创建好应用模块 python manage.py startapp tests 2、在models文件里创建模型 from django.db import modelsfrom book.models import User# Create your models here. class Tests(models.Model):STATUS_CHOICES ((0, 启用),(1, 停用),# 更多状态...)add_time mode…

RAR压缩包原密码过于简单如何修改密码?

RAR压缩包作为一种常见的文件压缩格式&#xff0c;广泛应用于文件传输和存储中。为了保障文件的安全性&#xff0c;用户经常会给RAR压缩包设置密码。但有时我们觉得原密码过于简单&#xff0c;想要修改成一个比较复杂的密码&#xff0c;这时我们要如何修改让人压缩包密码呢&…

<<编码>> 第 5 章 绕过拐弯的通信(Seeing Around Corners) 示例电路

灯泡通讯电路 info::操作说明 鼠标单击开关切换开合状态 primary::在线交互操作链接 https://cc.xiaogd.net/?startCircuitLinkhttps://book.xiaogd.net/code-hlchs-examples/assets/circuit/code-hlchs-ch05-01-communication-circuit.txt 双边灯泡通讯电路 info::操作说明 鼠…