责任链模式
- 一、介绍
- 二、实战日志记录系统
- 三、Servlet 过滤器源码分析
一、介绍
责任链模式是一种行为设计模式,它允许你将请求沿着处理者链进行传递,直到有一个处理者能够处理该请求。在这个模式中,每个处理者都包含对下一个处理者的引用,当收到请求时,它可以选择自行处理请求或者将请求传递给下一个处理者。
Java 中的责任链模式(Chain of Responsibility Pattern)是一种行为型设计模式,它通过给多个对象处理请求的机会,从而避免了请求的发送者与接收者之间的耦合关系。该模式将接收对象的对象连接成一条链,并沿着这条链传递请求,直到有一个对象处理它为止。
责任链模式主要包含以下几个角色:
- 抽象处理者(Handler): 定义一个处理请求的接口,并且包含一个指向下一个处理者的引用。
- 具体处理者(ConcreteHandler): 实现抽象处理者的接口,并在需要时处理请求或将请求传递给下一个处理者。
- 客户端(Client): 创建请求,并将其传递给链上的第一个具体处理者。
优点:
- 降低了请求发送者与接收者之间的耦合度。
- 可以动态地添加或修改处理者。
- 符合开放-封闭原则,可以在不修改现有代码的情况下扩展功能。
缺点:
- 如果处理者的数量过多,可能会影响性能。
- 调试可能会比较麻烦,因为请求可能会在链中传递多次。
二、实战日志记录系统
需求:开发一个日志记录系统,它需要处理三种类型的日志:错误日志、警告日志和信息日志。对于不同类型的日志,我们需要采取不同的行动,例如将错误日志记录到数据库、将警告日志发送电子邮件通知管理员,而对于信息日志只需要简单地打印到控制台即可。
// 抽象处理者interface LogHandler {void setNextHandler(LogHandler nextHandler);void handleLog(Log log);}// 具体处理者 - 错误日志处理者class ErrorLogHandler implements LogHandler {private LogHandler nextHandler;@Overridepublic void setNextHandler(LogHandler nextHandler) {this.nextHandler = nextHandler;}@Overridepublic 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;@Overridepublic void setNextHandler(LogHandler nextHandler) {this.nextHandler = nextHandler;}@Overridepublic 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;@Overridepublic void setNextHandler(LogHandler nextHandler) {this.nextHandler = nextHandler;}@Overridepublic 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);}}
LogHandler
接口定义了处理日志的方法handleLog
。它还包含一个setNextHandler
方法,用于设置链中的下一个处理者。ErrorLogHandler
、WarningLogHandler
和InfoLogHandler
是三个具体的处理者类,分别用于处理错误日志、警告日志和信息日志。每个处理者首先检查日志类型是否与自己负责的类型匹配,如果匹配则执行相应的操作;否则,将请求传递给链中的下一个处理者(如果存在)。Log
类表示一个日志对象,包含日志类型和日志消息。LogType
枚举定义了三种日志类型:错误、警告和信息。- 在客户端代码中,我们创建了三个具体处理者的实例,并将它们连接成一条链。然后,我们发送了三种不同类型的日志对象,这些日志对象将沿着链传递,直到被相应的处理者处理。
通过使用责任链模式,我们将日志处理的逻辑分散到不同的处理者中,每个处理者只需关注自己负责的日志类型。这种设计模式提高了代码的可维护性和可扩展性。如果需要添加新的日志类型或修改现有的处理逻辑,只需创建或修改相应的处理者类,而不需要修改其他代码。
此外,责任链模式还能够动态地组合和重组处理者链,以满足不同的需求。例如,在某些情况下,我们可能希望将某些类型的日志记录到文件中,而不是发送电子邮件或打印到控制台。在这种情况下,我们只需创建一个新的处理者类,并将其插入到处理者链中即可。
三、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);}
在这个方法中,我们可以看到两个重要的部分:
- 过滤器自己的处理逻辑。
- 调用
FilterChain
的doFilter()
方法,将请求传递给下一个过滤器或目标资源。
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
对象传递给过滤器。
当一个过滤器执行完自己的逻辑后,它会调用 FilterChain
的 doFilter()
方法,将请求传递给下一个过滤器。如果所有过滤器都执行完毕,则执行目标资源(如 Servlet)。
通过这种设计,过滤器就形成了一个责任链。每个过滤器都有机会对请求进行处理,并决定是继续执行下一个过滤器还是终止过滤器链。这种模式使得过滤器能够被动态地添加、删除或重新排序,而不会影响其他过滤器的执行。