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


    一、介绍

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

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

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

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

    优点:

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

    缺点:

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

    二、实战日志记录系统

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

    // 抽象处理者
    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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    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()。该方法的代码如下:

    public void doFilter(ServletRequest request, ServletResponse response,
                         FilterChain chain) throws IOException, ServletException {
        // 过滤器的自定义处理逻辑...
    
        // 将请求传递给下一个过滤器或目标资源
        chain.doFilter(request, response);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

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

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

    FilterChain 接口定义如下:

    public interface FilterChain {
        public void doFilter(ServletRequest request, ServletResponse response)
                throws IOException, ServletException;
    }
    
    • 1
    • 2
    • 3
    • 4

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

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

    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);
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

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

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

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

  • 相关阅读:
    K8s 里如何优雅地使用 /dev/shm 实现容器间共享内存
    C++ 基础与深度分析 Chapter10 泛型算法(bind、lambada表达式、泛型算法的改进ranges)
    Java项目:房屋租赁管理系统(java+SpringBoot+Vue+Maven+Mysql)
    算法训练 第一周
    Microsoft Dynamics CRM怎么样?
    Java开发学习(四十二)----MyBatisPlus查询语句之条件查询
    shiro 安全(权限)框架
    美国人学习Python,最受欢迎初级代码之十
    devc++ 使用 winsock 实现 UDP 局域网 WIFI 广播
    【经验】Ubuntu18.04切换Python版本及环境,及VScode/pdb调试方法(全)
  • 原文地址:https://blog.csdn.net/weixin_44716935/article/details/136648086