设计模式之职责链模式

职责链模式

定义

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

多个处理器依次处理同一个请求。一个请求先经过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请求和响应对象。粒度是
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值