• 责任链模式


    如果大家觉得文章有错误内容,欢迎留言或者私信讨论~

    原理和实现

    Avoid coupling the sender of a request to its receiver by giving more than one object a chance to handle the request. Chain the receiving objects and pass the request along the chain until an object handles it.

      翻译成中文就是:将请求的发送和接收解耦,让多个接收对象都有机会处理这个请求。将这些接收对象串成一条链,并沿着这条链传递这个请求,直到链上的某个接收对象能够处理它为止。

      在责任链中,多个处理器一依次处理同一个请求。一个请求先经过 A 处理器,随后经过 B 处理器处理完后传递给 C 处理器,以此类推,形成一个链条。每个链条上的处理器各司其职,这就叫做责任链模式

      第一种实现方式如下所示。其中,Handler 是所有处理器类的抽象父类,handle() 是抽象方法。每个具体的处理器类(HandlerA、HandlerB)的 handle() 函数的代码结构类似,如果它能处理该请求,就不继续往下传递;如果不能处理,则交由后面的处理器来处理(也就是调用 successor.handle())。HandlerChain 是处理器链,从数据结构的角度来看,它就是一个记录了链头、链尾的链表。其中,记录链尾是为了方便添加处理器。

    public abstract class Handler {
    	protected Handler successor = null;
    	public void setSuccessor(Handler successor) {
    		this.successor = successor;
    	}
    
    	public abstract void handle();
    }
    
    public class HandlerA extends Handler {
    	@Override
    	public boolean handle() {
    		boolean handled = false;
    		//...
    		if (!handled && successor != null) {
    			successor.handle();
    		}
    	}
    }
    
    public class HandlerB extends Handler {
    	@Override
    	public void handle() {
    		boolean handled = false;
    		//...
    		if (!handled && successor != null) {
    			successor.handle();
    		}
    	}
    }
    
    public class HandlerChain {
    	private Handler head = null;
    	private Handler tail = null;
    	public void addHandler(Handler handler) {
    		handler.setSuccessor(null);
    		if (head == null) {
    			head = handler;
    			tail = handler;
    			return;
    		} 
    		tail.setSuccessor(handler);
    		tail = handler;
    	} 
    	
    	public void handle() {
    		if (head != null) {
    			head.handle();
    		}
    	}
    }
    
    // 使用举例
    public class Application {
    	public static void main(String[] args) {
    		HandlerChain chain = new HandlerChain();
    		chain.addHandler(new HandlerA());
    		chain.addHandler(new HandlerB());
    		chain.handle();
    	}
    }
    
    
    • 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

      实际上,上面的实现方式不够优雅,不仅包含自己的业务逻辑,还包含对下一个处理器的调用,如果是不熟悉代码逻辑的新同学,很可能就会忘记去配置。针对这个问题,我们对代码进行重构,利用模板模式,将调用 successor.handle() 的逻辑从具体的处理器类中剥离出来,放到抽象父类中。

    public abstract class Handler {
    	protected Handler successor = null;
    	
    	public void setSuccessor(Handler successor) {
    		this.successor = successor;
    	} 
    	
    	public final void handle() {
    		boolean handled = doHandle();
    		if (successor != null && !handled) {
    			successor.handle();
    		}
    	} 
    	
    	protected abstract boolean doHandle();
    }
    
    public class HandlerA extends Handler {
    	@Override
    	protected boolean doHandle() {
    		boolean handled = false;
    		//...
    		return handled;
    	}
    }
    
    public class HandlerB extends Handler {
    	@Override
    	protected boolean doHandle() {
    		boolean handled = false;
    		//...
    		return handled;
    	}
    }
    
    
    // HandlerChain和Application代码不变
    
    • 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

      实际上,我们还可以通过数组的方式实现,这种方式更加简单,看到的这里的同学可以回头自己试一试,解题思路就在于 HandlerChain 里写成 List 集合形式即可。

    应用场景思考

      举个例子,比如 (User Generated Content,用户生成内容)的应用(比如论坛)来说,用户生成的内容(比如,在论坛中发表的帖子)可能会包含一些敏感词(比如涉黄、广告、反动等词汇)。针对这个场景,就可以使用责任链模式

      这里给出实例代码:

    public interface SensitiveWordFilter {
    	boolean doFilter(Content content);
    }
    
    public class SexyWordFilter implements SensitiveWordFilter {
    	@Override
    	public boolean doFilter(Content content) {
    		boolean legal = true;
    		//...
    		return legal;
    	}
    }
    // PoliticalWordFilter、AdsWordFilter类代码结构与SexyWordFilter类似
    
    public class SensitiveWordFilterChain {
    	private List<SensitiveWordFilter> filters = new ArrayList<>();
    	public void addFilter(SensitiveWordFilter filter) {
    		this.filters.add(filter);
    	} 
    	
    	// return true if content doesn't contain sensitive words.
    	public boolean filter(Content content) {
    		for (SensitiveWordFilter filter : filters) {
    			if (!filter.doFilter(content)) {
    				return false;
    			}
    		}	
    		return true;
    	}
    }
    
    public class ApplicationDemo {
    	public static void main(String[] args) {
    		SensitiveWordFilterChain filterChain = new SensitiveWordFilterChain();
    		filterChain.addFilter(new AdsWordFilter());
    		filterChain.addFilter(new SexyWordFilter());
    		filterChain.addFilter(new PoliticalWordFilter());
    		boolean legal = filterChain.filter(new Content());
    		if (!legal) {
    			// 不发表
    		} else {
    			// 发表
    		}
    	}
    }
    
    • 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

      你可能觉得这样写代码会有些过度设计,像下面这样也能够实现敏感词过滤功能。

    public class SensitiveWordFilter {
    	// return true if content doesn't contain sensitive words.
    	public boolean filter(Content content) {
    		if (!filterSexyWord(content)) {
    			return false;
    		} 
    		if (!filterAdsWord(content)) {
    			return false;
    		} 
    		if (!filterPoliticalWord(content)) {
    			return false;
    		} 
    		return true;
    	} 
    	private boolean filterSexyWord(Content content) {
    		//....
    	} 
    	private boolean filterAdsWord(Content content) {	
    		//...
    	} 
    	private boolean filterPoliticalWord(Content content) {
    		//...
    	}
    }
    
    
    • 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

      像责任链这种应用设计模式主要是为了应对代码的复杂性,让其满足开闭原则,提高代码的扩展性。这里**将大块代码逻辑拆分成函数,将大类拆分成小类,是应对代码复杂性的常用方法。**将各个功能独立出来,不耦合在一起,让代码易读,不会过于复杂。
      当我们要扩展新的过滤算法的时候,比如,我们还需要过滤特殊符号,按照非职责链模式的代码实现方式,我们需要修改 SensitiveWordFilter 的代码,违反开闭原则。而责任链模式,只需要新添加一个 Filter 类,并且通过 addFilter() 函数将它添加到 FilterChain 中即可,其他代码完全不需要修改,实现了开闭原则。

    框架中的责任链模式

    Servlert Filter

      Servlert Filter 是 Java Servlert 规范中定义的组件,翻译成中文就是过滤器。它能够实现对 HTTP 请求的过滤加工功能,比如鉴权、限流、记录日志等等功能。因为它是 Servlet 规范的一部分,所以,只要是支持 Servlet 的 Web 容器(比如,Tomcat、Jetty 等),都支持过滤器功能。如下图所示:
    在这里插入图片描述
      在实际项目中,我们该如何使用 Servlet Filter 呢?我写了一个简单的示例代码,如下所示。添加一个过滤器,我们只需要定义一个实现 javax.servlet.Filter 接口的过滤器类,并且将它配置在 web.xml 配置文件中。Web 容器启动的时候,会读取 web.xml 中的配置,创建过滤器对象。当有请求到来的时候,会先经过过滤器,然后才由 Servlet 来处理。

    public class LogFilter implements Filter {
    	@Override
    	public void init(FilterConfig filterConfig) throws ServletException {
    		// 在创建Filter时自动调用,
    		// 其中filterConfig包含这个Filter的配置参数,比如name之类的(从配置文件中读取的)
    	}
    
    	@Override
    	public void doFilter(ServletRequest request, ServletResponse response, Filte
    		System.out.println("拦截客户端发送来的请求.");
    		chain.doFilter(request, response);
    		System.out.println("拦截发送给客户端的响应.");
    	}
    
    	@Override
    	public void destroy() {
    		// 在销毁Filter时自动调用
    	}	
    }
    
    // 在web.xml配置文件中如下配置:
    <filter>
    	<filter-name>logFilter</filter-name>
    	<filter-class>com.xzg.cd.LogFilter</filter-class>
    </filter>
    <filter-mapping>
    	<filter-name>logFilter</filter-name>
    	<url-pattern>/*
    
    
    • 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

      从刚刚的代码演示中,我们可以看到新增一个过滤链非常轻松,不需要修改任何代码,定义一个实现 javax.servlet.Filter 的类,再改改配置就搞定了,完全符合开闭原则,这就是利用了责任链模式做到的。
      在上一节课中,我们讲到,职责链模式的实现包含处理器接口(IHandler)或抽象类(Handler),以及处理器链(HandlerChain)。对应到 Servlet Filter,javax.servlet.Filter 就是处理器接口,FilterChain 就是处理器链。接下来,我们重点来看FilterChain 是如何实现的。

    // 只保留了重要部分,关键代码请到 Tomcat 源码中自行查看
    public final class ApplicationFilterChain implements FilterChain {
    	private int pos = 0; //当前执行到了哪个filter
    	private int n; //filter的个数
    	private ApplicationFilterConfig[] filters;
    	private Servlet servlet;
    	
    	@Override
    	public void doFilter(ServletRequest request, ServletResponse response) {
    		if (pos < n) {
    			ApplicationFilterConfig filterConfig = filters[pos++];
    			Filter filter = filterConfig.getFilter();
    			// 递归调用
    			filter.doFilter(request, response, this);
    		} else {
    			// filter都处理完毕后,执行servlet
    			servlet.service(request, response);
    		}
    	}
    	
    	public void addFilter(ApplicationFilterConfig filterConfig) {
    		for (ApplicationFilterConfig filter:filters)
    			if (filter==filterConfig)
    				return;
    			if (n == filters.length) {//扩容
    				ApplicationFilterConfig[] newFilters = new ApplicationFilterConfig[n + IN
    				System.arraycopy(filters, 0, newFilters, 0, n);
    				filters = newFilters;
    			}
    		filters[n++] = filterConfig;
    	}	
    }
    
    • 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

    Spring Interceptor

      刚刚讲了 Java Servlert ,现在我们来讲一讲 Spring Interceptor 。Spring Interceptor 是 Spring MVC 框架的一部分,由 Spring MVC 框架来提供实现。客户端发送的请求,会先经过 Servlet Filter,然后再经过 Spring Interceptor,最后到达具体的业务代码中:
    在这里插入图片描述
      那么我们在项目中应该如何使用它呢?我写了一个简单的示例代码,如下所示。在 Java Servlert 对请求和响应的处理可以放到 doFilter() 中处理,而 LogInterceptor对请求的拦截在 preHandle() 中实现,对响应的拦截在 postHandle() 中实现。

    public class LogInterceptor implements HandlerInterceptor {
    	@Override
    	public boolean preHandle(HttpServletRequest request, HttpServletResponse resp
    		System.out.println("拦截客户端发送来的请求.");
    		return true; // 继续后续的处理
    	}
    	
    	@Override
    	public void postHandle(HttpServletRequest request, HttpServletResponse respon
    		System.out.println("拦截发送给客户端的响应.");
    	}
    
    	
    	@Override
    	public void afterCompletion(HttpServletRequest request, HttpServletResponse
    		System.out.println("这里总是被执行.");
    	}
    }
    
    //在Spring MVC配置文件中配置interceptors
    <mvc:interceptors>
    	<mvc:interceptor>
    		<mvc:mapping path="/*"/>
    		<bean class="com.xzg.cd.LogInterceptor" />
    	</mvc:interceptor>
    </mvc:interceptors>
    
    • 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

      同样,我们还是来剖析一下,Spring Interceptor 底层是如何实现的。
     &esmp;当然,它也是基于职责链模式实现的。其中,HandlerExecutionChain 类是职责链模式中的处理器链。它的实现相较于 Tomcat 中的 ApplicationFilterChain 来说,逻辑更加清晰,不需要使用递归来实现,主要是因为它将请求和响应的拦截工作,拆分到了两个函数中实现。

    public class HandlerExecutionChain {
    	private final Object handler;
    	private HandlerInterceptor[] interceptors;
    
    	public void addInterceptor(HandlerInterceptor interceptor) {
    		initInterceptorList().add(interceptor);
    	}
    
    	boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
    		for (int i = 0; i < this.interceptorList.size(); i++) {
    			HandlerInterceptor interceptor = this.interceptorList.get(i);
    			if (!interceptor.preHandle(request, response, this.handler)) {
    				triggerAfterCompletion(request, response, null);
    				return false;
    			}
    			this.interceptorIndex = i;
    		}
    		return true;
    	}
    
    	void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv)
    			throws Exception {
    		for (int i = this.interceptorList.size() - 1; i >= 0; i--) {
    			HandlerInterceptor interceptor = this.interceptorList.get(i);
    			interceptor.postHandle(request, response, this.handler, mv);
    		}
    	}
    
    	void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex) {
    		for (int i = this.interceptorIndex; i >= 0; i--) {
    			HandlerInterceptor interceptor = this.interceptorList.get(i);
    			try {
    				interceptor.afterCompletion(request, response, this.handler, ex);
    			}
    			catch (Throwable ex2) {
    				logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
    			}
    		}
    	}
    	
    }
    
    • 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

      在 Spring 框架中,DispatcherServlet 的 doDispatch() 方法来分发请求,它在真正的业务逻辑执行前后,执行 HandlerExecutionChain 中的 applyPreHandle() 和 applyPostHandle() 函数,用来实现拦截的功能。具体的代码实现很简单,你自己应该能脑补出来,这里就不罗列了。感兴趣的话,你可以自行去查看。

  • 相关阅读:
    Alibaba Cloud Linux 3安装Docker
    《对比Excel,轻松学习Python数据分析》读书笔记------数据分组与数据透视表
    如何理解CDN?说说实现原理?
    4-2计算小于1000的正整数的平方根
    java计算机毕业设计江智能股票推荐系统MyBatis+系统+LW文档+源码+调试部署
    攻防世界 高手进阶区-WEB
    Python面试题
    第十五篇,STM32的SPI串行通信协议
    为什么红黑树的效率比较高?
    LBA转换成CHS公式
  • 原文地址:https://blog.csdn.net/qq_43654226/article/details/126823284