设计模式之职责链模式

作者简介:大家好,我是smart哥,前中兴通讯、美团架构师,现某互联网公司CTO

联系qq:184480602,加我进群,大家一起学习,一起进步,一起对抗互联网寒冬

学习必须往深处挖,挖的越深,基础越扎实!

阶段1、深入多线程

阶段2、深入多线程设计模式

阶段3、深入juc源码解析

阶段4、深入jdk其余源码解析

阶段5、深入jvm源码解析

码哥源码部分

码哥讲源码-原理源码篇【2024年最新大厂关于线程池使用的场景题】

码哥讲源码【炸雷啦!炸雷啦!黄光头他终于跑路啦!】

码哥讲源码-【jvm课程前置知识及c/c++调试环境搭建】

​​​​​​码哥讲源码-原理源码篇【揭秘join方法的唤醒本质上决定于jvm的底层析构函数】

码哥源码-原理源码篇【Doug Lea为什么要将成员变量赋值给局部变量后再操作?】

码哥讲源码【你水不是你的错,但是你胡说八道就是你不对了!】

码哥讲源码【谁再说Spring不支持多线程事务,你给我抽他!】

终结B站没人能讲清楚红黑树的历史,不服等你来踢馆!

打脸系列【020-3小时讲解MESI协议和volatile之间的关系,那些将x86下的验证结果当作最终结果的水货们请闭嘴】

职责链模式

定义

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

多个处理器依次处理同一个请求。一个请求先经过A处理器处理,然后再把请求传递给B处理器,B处理完后传递给C,以此类推,形成一个链条。链条上的每个处理器各自承担各自的处理职责。职责链模式实现有多种。

类图

实现

实现一个敏感词过滤。

1. 定义过滤算法接口

    public interface SensitiveWordFilter {
        boolean doFilter(Content content);
    }

2. 定义过滤算法实现类

    /**
     * 敏感词过滤一
     */
    public class SexyWordFilter implements SensitiveWordFilter{
        @Override
        public boolean doFilter(Content content) {
            if (content.content.contains("性")) {
                return false;
            }
            return true;
        }
    }

    /**
     * 敏感词过滤二
     */
    public class PoliticalWorldFilter implements SensitiveWordFilter{
        @Override
        public boolean doFilter(Content content) {
            if (content.content.contains("政治")) {
                return false;
            }
            return true;
        }
    }

3. 创建Filter Chain

    /**
     * 通过集合保存过滤算法
     * 暴露filter执行敏感词过滤。这只是其中的职责链的变种。
     */
    public class SenstiveWordFilterChain {
        private List<SensitiveWordFilter> filterList = new ArrayList<>();
        public void addWordFilter(SensitiveWordFilter filter) {
            this.filterList.add(filter);
        }
    
        public boolean filter(Content content) {
            for (SensitiveWordFilter sensitiveWordFilter : filterList) {
                if (!sensitiveWordFilter.doFilter(content)) {
                    return false;
                }
            }
            return true;
        }
    }

4. Main

    /**
     * 1. 职责链模式很好体现单一职责原则。业务逻辑判断 设计成独立的类,通过接口方便扩展。
     * 2. 职责链模式也体现开闭原则。如果需要新添加过滤算法,我们只需要实现Filter接口,并通过addWordFitler添加到职责链即可。其它代码不需要修改,进而提高代码的扩展性。
     */
    public class M {
        public static void main(String[] args) {
            SenstiveWordFilterChain senstiveWordFilterChain = new SenstiveWordFilterChain();
            senstiveWordFilterChain.addWordFilter(new PoliticalWorldFilter());
            senstiveWordFilterChain.addWordFilter(new SexyWordFilter());
            Content content = new Content();
            content.content = "2333";
            boolean filter = senstiveWordFilterChain.filter(content);
            if (filter) {
                System.out.println("可以正常发布");
            } else {
                System.out.println("不允许发布。因为包含敏感信息!");
            }
        }
    }

Servlet Filter

Servlet FilterJava Servlet规范中定义的组件。它可以对HTTP请求进行过滤,比如鉴权、限流、日志记录、参数验证等。是Servlet规范的一部分。所以,只要是支持Servlet的Web容器(Tomcat、Jetty),都支持过滤器功能。

步骤:

  1. 实现Filter
  2. Web.xml配置文件中添加filter-mapping

实现

1. 定义Filter实现类
    public class LogFilter implements Filter{
        @Override
        public void init(FilterConfig filterConfig) throws ServletException {
            // 在创建Filter时调用
        }
    
        @Override
        public void doFilter(ServletRequest request, ServletResponse response,
                             FilterChain chain) throws IOException, ServletException {
            // 拦截客户端发送的请求
            chain.doFilter(request, response);
            // 拦截发送给客户端的响应
        }
        
        @Override
        public void destory() {
            // 在销毁filter时调用
        }
    }
2. Tomcat提供FilterChain的实现类
    public final class ApplicationFilterChain implements FilterChain {
        // 当前执行到了哪个filter
        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;
        }
    }

doFilter实现比较有技巧性。使用递归保留之前过滤器的快照,目的是支持双向拦截,既能拦截客户端发送的请求,也能拦截发送给客户端的响应。你可以把这个递归操作看成压栈操作。

Spring Interceptor

可以和Servlet Filter看作一个概念,都是用来对HTTP请求进行拦截处理。不同之处在于

  1. Servlet FilterServlet规范的一部分,实现依赖于Web容器。而Spring InterceptorSpring MVC的一部分,由Spring框架提供实现。
  2. 客户端的请求,先经过Servlet Filter,再经过Spring Interceptor,最后到达具体业务代码。

实现

    public class LogInterceptor implements HandlerInterceptor {
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response) {
            System.out.println("拦截客户端发送来的请求.");
            return true; // 继续后续的处理
        }
        
        @Override
        public void postHandle(HttpServletRequest request, HttpServletResponse response) {
       		System.out.println("拦截发送给客户端的响应.");
        }
        
        @Override
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response) {
       		System.out.println("这里总是被执行.");
        
        }
    }

    <!--在Spring MVC配置文件中配置interceptors-->
    <mvc:interceptors>
        <mvc:interceptor>
            <mvc:mapping path="/*"/>
            <bean class="com.xx.LogInterceptor" />
        </mvc:interceptor>
    </mvc:interceptors>

同样,Spring Interceptor的底层也是基于职责链模式实现。其中,HandlerExecutionChain是职责链中的处理器链。它的实现相校于Tomcat,逻辑更加清晰,没有递归。主要原因是它将请求和响应的拦截工作拆分到了两个函数中实现。源码如下(做了裁剪):

    public class HandlerExecutionChain {
        private final Object handler;
        private HandlerInterceptor[] interceptors;
    
        public void addInterceptor(HandlerInterceptor interceptor) {
            initInterceptorList().add(interceptor);
        }
    	
        boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) {
            HandlerInterceptor[] interceptors = getInterceptors();
            if (!ObjectUtils.isEmpty(interceptors)) {
                for (int i = 0; i < interceptors.length; i++) {
                    HandlerInterceptor interceptor = interceptors[i];
                    if (!interceptor.preHandle(request, response, this.handler)) {
                        triggerAfterCompletion(request, response, null);
                        return false;
                    }
                }
            }
            return true;
        }
    	// 在真正执行业务逻辑前,执行此函数
        void applyPostHandle(HttpServletRequest request, HttpServletResponse response) {
            HandlerInterceptor[] interceptors = getInterceptors();
            if (!ObjectUtils.isEmpty(interceptors)) {
                for (int i = interceptors.length - 1; i >= 0; i--) {
                    HandlerInterceptor interceptor = interceptors[i];
                    interceptor.postHandle(request, response, this.handler, mv);
                }
            }
    
        }
    	
        // 在真正执行业务逻辑后,执行此函数
        void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response) throws Exception {
            HandlerInterceptor[] interceptors = getInterceptors();
            if (!ObjectUtils.isEmpty(interceptors)) {
                for (int i = this.interceptorIndex; i >= 0; i--) {
                    HandlerInterceptor interceptor = interceptors[i];
                    try {
                        interceptor.afterCompletion(request, response, this.handler, ex);
                    } catch (Throwable ex2) {
                        logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
                    }
                }
            }
        }
    }

总结

  1. 职责链很好地符合单一职责原则(每个算法都是独立的实现)开闭原则(扩展算法不需要修改其他代码,只需要修改客户端代码。也可以在类中做标记,比如实现某个标记接口,然后通过Spring的Bean扫描并装载到职责链内)

  2. 职责链本意是依次处理同一个请求,用户可以自定义该请求是否需要经历所有处理器,不会中途停止; 还是说当某个处理器有能力处理请求就完成职责链对该请求的处理。

  3. 职责链具有复用扩展能力。在实际的项目开发中比较常用。常用在框架开发中,用来实现框架的过滤器、拦截器功能,让框架的使用者在不需要修改框架源码的情况下,添加新的拦截功能。

  4. Double Filter、Netty ChannelPipeline也是职责链模式的实际应用案例。

  5. AOP、Servlet Filter、Spring Interceptor三者区别

    1. Servlet Filter可以获取原始HTTP请求,却无法获取请求的控制器(Controller)以及方法信息。粒度是HttpRequest、HttpResponse
    2. Spring Interceptor可以拿到请求的控制器和方法,无法获取到请求方法的参数。粒度是HttpRequest、HttpResponse
    3. Spring AOP有能力获取方法的参数,却无法获取HTTP请求和响应对象。粒度是
相关推荐
思忖小下11 分钟前
梳理你的思路(从OOP到架构设计)_设计模式Template Method模式
设计模式·模板方法模式·eit
思忖小下10 小时前
梳理你的思路(从OOP到架构设计)_简介设计模式
设计模式·架构·eit
liyinuo201712 小时前
嵌入式(单片机方向)面试题总结
嵌入式硬件·设计模式·面试·设计规范
aaasssdddd9615 小时前
C++的封装(十四):《设计模式》这本书
数据结构·c++·设计模式
T1an-115 小时前
设计模式之【观察者模式】
观察者模式·设计模式
思忖小下16 小时前
梳理你的思路(从OOP到架构设计)_设计模式Factory Method模式
设计模式·工厂方法模式·eit
霁月风17 小时前
设计模式——工厂方法模式
c++·设计模式·工厂方法模式
发飙的蜗牛'20 小时前
23种设计模式
android·java·设计模式
NorthCastle1 天前
设计模式-创建型模式-简单工厂模式详解
设计模式·简单工厂模式
越甲八千1 天前
重拾设计模式-外观模式和适配器模式的异同
设计模式·适配器模式·外观模式