Java责任链模式源码剖析及使用场景

news/2024/5/19 14:50:39 标签: 责任链模式, 设计模式, java

责任链模式

  • 一、介绍
  • 二、实战日志记录系统
  • 三、Servlet 过滤器源码分析

一、介绍

责任链模式是一种行为设计模式,它允许你将请求沿着处理者链进行传递,直到有一个处理者能够处理该请求。在这个模式中,每个处理者都包含对下一个处理者的引用,当收到请求时,它可以选择自行处理请求或者将请求传递给下一个处理者。

Java 中的责任链模式(Chain of Responsibility Pattern)是一种行为型设计模式,它通过给多个对象处理请求的机会,从而避免了请求的发送者与接收者之间的耦合关系。该模式将接收对象的对象连接成一条链,并沿着这条链传递请求,直到有一个对象处理它为止。

责任链模式主要包含以下几个角色:

  1. 抽象处理者(Handler): 定义一个处理请求的接口,并且包含一个指向下一个处理者的引用。
  2. 具体处理者(ConcreteHandler): 实现抽象处理者的接口,并在需要时处理请求或将请求传递给下一个处理者。
  3. 客户端(Client): 创建请求,并将其传递给链上的第一个具体处理者。

优点:

  • 降低了请求发送者与接收者之间的耦合度。
  • 可以动态地添加或修改处理者。
  • 符合开放-封闭原则,可以在不修改现有代码的情况下扩展功能。

缺点:

  • 如果处理者的数量过多,可能会影响性能。
  • 调试可能会比较麻烦,因为请求可能会在链中传递多次。

二、实战日志记录系统

需求:开发一个日志记录系统,它需要处理三种类型的日志:错误日志、警告日志和信息日志。对于不同类型的日志,我们需要采取不同的行动,例如将错误日志记录到数据库、将警告日志发送电子邮件通知管理员,而对于信息日志只需要简单地打印到控制台即可。

java">// 抽象处理者
interface LogHandler {
    void setNextHandler(LogHandler nextHandler);
    void handleLog(Log log);
}

// 具体处理者 - 错误日志处理者
class ErrorLogHandler implements LogHandler {
    private LogHandler nextHandler;

    @Override
    public void setNextHandler(LogHandler nextHandler) {
        this.nextHandler = nextHandler;
    }

    @Override
    public void handleLog(Log log) {
        if (log.getLogType() == LogType.ERROR) {
            // 将错误日志记录到数据库
            System.out.println("Error log recorded in database: " + log.getMessage());
        } else if (nextHandler != null) {
            nextHandler.handleLog(log);
        }
    }
}

// 具体处理者 - 警告日志处理者
class WarningLogHandler implements LogHandler {
    private LogHandler nextHandler;

    @Override
    public void setNextHandler(LogHandler nextHandler) {
        this.nextHandler = nextHandler;
    }

    @Override
    public void handleLog(Log log) {
        if (log.getLogType() == LogType.WARNING) {
            // 发送警告邮件给管理员
            System.out.println("Warning email sent to admin: " + log.getMessage());
        } else if (nextHandler != null) {
            nextHandler.handleLog(log);
        }
    }
}

// 具体处理者 - 信息日志处理者
class InfoLogHandler implements LogHandler {
    private LogHandler nextHandler;

    @Override
    public void setNextHandler(LogHandler nextHandler) {
        this.nextHandler = nextHandler;
    }

    @Override
    public void handleLog(Log log) {
        if (log.getLogType() == LogType.INFO) {
            // 打印信息日志到控制台
            System.out.println("Info log: " + log.getMessage());
        } else if (nextHandler != null) {
            nextHandler.handleLog(log);
        }
    }
}

// 日志对象
class Log {
    private LogType logType;
    private String message;

    public Log(LogType logType, String message) {
        this.logType = logType;
        this.message = message;
    }

    public LogType getLogType() {
        return logType;
    }

    public String getMessage() {
        return message;
    }
}

// 日志类型枚举
enum LogType {
    ERROR, WARNING, INFO
}

// 客户端代码
public class Client {
    public static void main(String[] args) {
        // 创建处理者链
        LogHandler errorHandler = new ErrorLogHandler();
        LogHandler warningHandler = new WarningLogHandler();
        LogHandler infoHandler = new InfoLogHandler();

        errorHandler.setNextHandler(warningHandler);
        warningHandler.setNextHandler(infoHandler);

        // 发送不同类型的日志
        Log errorLog = new Log(LogType.ERROR, "Error occurred in database connection.");
        Log warningLog = new Log(LogType.WARNING, "Disk space is running low.");
        Log infoLog = new Log(LogType.INFO, "Application started successfully.");

        errorHandler.handleLog(errorLog);
        errorHandler.handleLog(warningLog);
        errorHandler.handleLog(infoLog);
    }
}
  1. LogHandler 接口定义了处理日志的方法 handleLog。它还包含一个 setNextHandler 方法,用于设置链中的下一个处理者。
  2. ErrorLogHandlerWarningLogHandlerInfoLogHandler 是三个具体的处理者类,分别用于处理错误日志、警告日志和信息日志。每个处理者首先检查日志类型是否与自己负责的类型匹配,如果匹配则执行相应的操作;否则,将请求传递给链中的下一个处理者(如果存在)。
  3. Log 类表示一个日志对象,包含日志类型和日志消息。
  4. LogType 枚举定义了三种日志类型:错误、警告和信息。
  5. 在客户端代码中,我们创建了三个具体处理者的实例,并将它们连接成一条链。然后,我们发送了三种不同类型的日志对象,这些日志对象将沿着链传递,直到被相应的处理者处理。

通过使用责任链模式,我们将日志处理的逻辑分散到不同的处理者中,每个处理者只需关注自己负责的日志类型。这种设计模式提高了代码的可维护性和可扩展性。如果需要添加新的日志类型或修改现有的处理逻辑,只需创建或修改相应的处理者类,而不需要修改其他代码。

此外,责任链模式还能够动态地组合和重组处理者链,以满足不同的需求。例如,在某些情况下,我们可能希望将某些类型的日志记录到文件中,而不是发送电子邮件或打印到控制台。在这种情况下,我们只需创建一个新的处理者类,并将其插入到处理者链中即可。

三、Servlet 过滤器源码分析

责任链模式在 Java 中有很多应用场景,例如 Servlet 过滤器、Java Web 开发中的拦截器(Interceptor)、安全框架中的身份验证等。Spring 框架的异常处理机制 HandlerExceptionResolver 就是一个典型的责任链模式的应用。

Servlet 过滤器(Filter)在 Servlet 规范中就采用了责任链模式的设计。当一个请求到达 Web 应用程序时,它会被传递给一系列的过滤器进行处理,每个过滤器都有机会对请求进行处理,或者将其传递给下一个过滤器。这种设计模式使得过滤器可以被动态地添加或删除,而不会影响其他过滤器的执行。

下面我们来分析一下 Servlet 过滤器是如何实现责任链模式的。

在 Servlet 规范中,Filter 接口定义了过滤器的基本方法,其中最重要的方法是 doFilter()。该方法的代码如下:

java">public void doFilter(ServletRequest request, ServletResponse response,
                     FilterChain chain) throws IOException, ServletException {
    // 过滤器的自定义处理逻辑...

    // 将请求传递给下一个过滤器或目标资源
    chain.doFilter(request, response);
}

在这个方法中,我们可以看到两个重要的部分:

  1. 过滤器自己的处理逻辑。
  2. 调用 FilterChaindoFilter() 方法,将请求传递给下一个过滤器或目标资源。

FilterChain 接口定义如下:

java">public interface FilterChain {
    public void doFilter(ServletRequest request, ServletResponse response)
            throws IOException, ServletException;
}

这个接口只有一个 doFilter() 方法,用于将请求传递给下一个过滤器或目标资源。

在 Servlet 容器中,FilterChain 的实现类 ApplicationFilterChain 维护了一个过滤器列表,并负责按顺序调用每个过滤器的 doFilter() 方法。ApplicationFilterChain 的部分代码如下:

java">public final class ApplicationFilterChain implements FilterChain {
    private int pos = 0;
    private int n;
    private ApplicationFilterConfig[] filterConfigs;

    // ...

    public void doFilter(ServletRequest request, ServletResponse response)
            throws IOException, ServletException {
        if (pos < n) {
            ApplicationFilterConfig filterConfig = filterConfigs[pos++];
            Filter filter = filterConfig.getFilter();
            filter.doFilter(request, response, this);
        } else {
            // 如果没有更多过滤器,则执行目标资源
            servlet.service(request, response);
        }
    }
}

在上面的代码中,ApplicationFilterChain 维护了一个 filterConfigs 数组,存储了所有需要执行的过滤器。在 doFilter() 方法中,它会依次调用每个过滤器的 doFilter() 方法,并将自身作为 FilterChain 对象传递给过滤器。

当一个过滤器执行完自己的逻辑后,它会调用 FilterChaindoFilter() 方法,将请求传递给下一个过滤器。如果所有过滤器都执行完毕,则执行目标资源(如 Servlet)。

通过这种设计,过滤器就形成了一个责任链。每个过滤器都有机会对请求进行处理,并决定是继续执行下一个过滤器还是终止过滤器链。这种模式使得过滤器能够被动态地添加、删除或重新排序,而不会影响其他过滤器的执行。


http://www.niftyadmin.cn/n/5423776.html

相关文章

sentinel prometheus指标收集及资源规则正则表达式实现

sentinel 支持 prometheus 收集指标 实现原理 在 sentinel-extension 模块下&#xff0c;新增 sentinel-prometheus-metric-exporter 模块。依赖Prometheus 提供的 simpleclient 和 simpleclient_httpserver 来实现 exporter。 依赖 simpleclient 主要是为了实现自定义Collect…

Vue3全家桶 - Vue3 - 【3】模板语法(指令+修饰符 + v-model语法糖)

一、模板语法 主要还是记录一些指令的使用和vue2的区别&#xff1b;vue3指令导航&#xff1b; 1.1 v-text 和 v-html 指令的区别&#xff1a; v-text&#xff1a; 更新元素的文本内容&#xff1b;v-text 通过设置元素的 textContent 属性来工作&#xff0c;因此它将覆盖元素…

【嵌入式】嵌入式系统稳定性建设:最后的防线

&#x1f9d1; 作者简介&#xff1a;阿里巴巴嵌入式技术专家&#xff0c;深耕嵌入式人工智能领域&#xff0c;具备多年的嵌入式硬件产品研发管理经验。 &#x1f4d2; 博客介绍&#xff1a;分享嵌入式开发领域的相关知识、经验、思考和感悟。提供嵌入式方向的学习指导、简历面…

qt如何将QHash中的数据有序地放入到QList中

在qt中&#xff0c;要将QHash中的数据有序地放入到QList中&#xff0c;首先要明白&#xff1a; 我们可以遍历QHash中的键值对&#xff0c;并将其按照键的顺序或值的大小插入到QList中&#xff0c;直接用for循环即可。 #include <QCoreApplication> #include <QHas…

深入理解Nginx日志级别

Nginx 是一个高性能的 HTTP 和反向代理服务器&#xff0c;广泛用于提供网站和应用服务。它的强大功能之一是灵活的日志记录能力&#xff0c;允许管理员根据需要配置不同的日志级别。正确理解和使用这些日志级别对于监控、调试和保障你的服务稳定运行至关重要。本文旨在深入介绍…

【Datawhale学习笔记】从大模型到AgentScope

从大模型到AgentScope AgentScope是一款全新的Multi-Agent框架&#xff0c;专为应用开发者打造&#xff0c;旨在提供高易用、高可靠的编程体验&#xff01; 高易用&#xff1a;AgentScope支持纯Python编程&#xff0c;提供多种语法工具实现灵活的应用流程编排&#xff0c;内置…

20240309web前端_第一周作业_完成用户注册界面

作业一&#xff1a;完成用户注册界面 成果展示&#xff1a; 完整代码&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-…

AIGC——DreamTuner通过单张图片生成与该图片主题风格一致的新图像

简介 DreamTuner的能力在于从单个图像生成主体驱动的新通用方法&#xff0c;这意味着用户只需提供一张图片&#xff0c;DreamTuner就能帮助他们生成与原始图片在主题和风格上一致的新图像。 算法重要之处在于其通用性和个性化定制的能力。无论是需要根据特定主题或条件创建个…