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. 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. 调用 FilterChaindoFilter() 方法,将请求传递给下一个过滤器或目标资源。

FilterChain 接口定义如下:

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

这个接口只有一个 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);
        }
    }
}

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

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

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

  • 20
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Java语录精选

你的鼓励是我坚持下去的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值