【从优秀源码中学习设计模式】--- 责任链模式

前言

本文以Java语言为主,分析包括JDK、Spring、MyBatis、Guava、org.apache.xxx中,等一些优秀的开源代码、项目,在这些开源代码、项目中都包含了大量的设计模式,通过对它们进行分析,能够快速帮助我们学会设计模式的使用方式,由理论过渡到实践中,进而真正了解设计模式的思想,由于内容较多,所以每个设计模式单独写一篇文章,需要了解其他模式请点击对应链接跳转。

建造者模式
装饰器模式
适配器模式
策略模式
责任链模式
模板方法模式

描述

责任链模式也是非常实用的一种设计模式,尤其是在许多框架开发中都有应用,因为它可以用来提供框架的扩展点,一种非常经典的应用场景类型就是过滤器、拦截器。

责任链模式是一种行为型的设计模式,主要是为了将请求的发送和接收解耦,这样就可以方便扩展多个对象来处理请求,每个对象之间的处理顺序可以自由调配,每个对象处理后的数据也可以互相传递。

标准结构

在这里插入图片描述

public abstract class Handler {

    private Handler successor;

    public void setSuccessor(Handler successor) {
        this.successor = successor;
    }

    public Handler getSuccessor() {
        return successor;
    }

    public abstract void handleRequest(String msg);

}
import org.apache.commons.lang3.StringUtils;

public class ConcreteHandlerA extends Handler {

    @Override
    public void handleRequest(String msg) {
        if (StringUtils.isNotEmpty(msg)) {
            System.out.println("ConcreteHandlerA pass");
            if (getSuccessor() != null) {
                getSuccessor().handleRequest(msg);
            }
        } else {
            System.out.println("msg 不能为空!");
        }
    }
}
public class ConcreteHandlerB extends Handler {

    @Override
    public void handleRequest(String msg) {
        if (msg.length() <= 16) {
            System.out.println("ConcreteHandlerB pass");
            if (getSuccessor() != null) {
                getSuccessor().handleRequest(msg);
            }
        } else {
            System.out.println("msg长度不能超过16!");
        }
    }
}
public class Main {
    public static void main(String[] args) {
        Handler handler1 = new ConcreteHandlerA();
        Handler handler2 = new ConcreteHandlerB();
        handler1.setSuccessor(handler2);
        handler1.handleRequest("text");
    }
}

使用场景

前面有介绍了,责任链模式主要就是为了避免请求发送者与接收者耦合在一起,让多个对象都有可能接收请求,将这些对象连接成一条链,并且沿着这条链传递请求,直到有对象处理它为止。

因此责任链模式在针对这种需要多个对象配合处理的情况下非常好用,比如:多级别的审批流程、多级别的数据安全防护处理,还有框架中常见的过滤器、拦截器等。

优点

  1. 降低耦合度,它将请求的发送者和接收者解耦。

  2. 简化了对象,使得对象不需要知道链的结构。

  3. 增强给对象指派职责的灵活性,通过改变链内的成员或者调动它们的次序,允许动态地新增或者删除责任。

  4. 增加新的请求处理类很方便。

缺点

  1. 不能保证请求一定被接收。
  2. 系统性能将受到一定影响,而且在进行代码调试时不太方便,可能会造成循环调用。
  3. 责任链容易配置错误,增加了代码的出错风险。

使用案例

Servlet Filter

Servlet规范中有一个非常经典的设计就是Filter,它主要用来实现对HTTP请求的过滤,比如:鉴权、限流、参数校验、记录日志等等。

在这里插入图片描述
Filter接口

Filter就相当于Handler


public interface Filter {

	public void init(FilterConfig filterConfig) throws ServletException;
	
    public void doFilter ( ServletRequest request, ServletResponse response, FilterChain chain ) throws IOException, ServletException;

	public void destroy();
}

FilterChain

FilterChain相当于前面main方法中的逻辑,通常会做一层封装,封装后就是HandlerChain.

public interface FilterChain {

    public void doFilter ( ServletRequest request, ServletResponse response ) throws IOException, ServletException;

}

我们知道Servlet只定义标准,具体实现由各个web容器来完成,比如ApplicationFilterChain,就是tomcat中提供的实现类。

    @Override
    public void doFilter(ServletRequest request, ServletResponse response)
        throws IOException, ServletException {

        // 省略部分代码...
        
        internalDoFilter(request,response);
        
    }
    private void internalDoFilter(ServletRequest request,
                                  ServletResponse response)
        throws IOException, ServletException {

        // Call the next filter if there is one
        if (pos < n) {
            ApplicationFilterConfig filterConfig = filters[pos++];
           
            Filter filter = filterConfig.getFilter();
				
			// 省略部分代码...
			// 通过递归 + pos下标的方式,遍历处理所有的filter	
            filter.doFilter(request, response, this);
                
            
            return;
        }
        
        // 省略部分代码...
        servlet.service(request, response);
        
    }

给filterChain添加filter

for (FilterMap filterMap : filterMaps) {
    if (!matchDispatcher(filterMap, dispatcher)) {
        continue;
    }
    if (!matchFiltersURL(filterMap, requestPath))
        continue;
    ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
            context.findFilterConfig(filterMap.getFilterName());
    if (filterConfig == null) {
        // FIXME - log configuration problem
        continue;
    }
    filterChain.addFilter(filterConfig);
}

Spring Interceptor

Spring Interceptor和Servlet Filter差不多,是spring框架中的功能,当客户端的请求发送过来后,会先经过Servlet Filter再经过Spring Interceptor,最后才到达业务代码中。

HandlerInterceptor

HandlerInterceptor接口相当于Handler

public interface HandlerInterceptor {

	default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {

		return true;
	}

	default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
			@Nullable ModelAndView modelAndView) throws Exception {
	}

	default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
			@Nullable Exception ex) throws Exception {
	}

}

HandlerExecutionChain

HandlerExecutionChain接口相当于HandlerChain

public class HandlerExecutionChain {

	private final Object handler;
	private final List<HandlerInterceptor> interceptorList = new ArrayList<>();
	private int interceptorIndex = -1;

	public void addInterceptor(HandlerInterceptor interceptor) {
		this.interceptorList.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);
			}
		}
	}
}



hutool Chain

public static void main(String[] args) {
    CharSequence str = "<html><p>hello world</p></html>";
    String escape = EscapeUtil.escapeHtml4(str);
    System.out.println(escape);
    String unescape = EscapeUtil.unescapeHtml4(escape);
    System.out.println(unescape);
}

输出结果

&lt;html&gt;&lt;p&gt;hello world&lt;/p&gt;&lt;/html&gt;
<html><p>hello world</p></html>

escapeHtml4方法可以转义html中的特殊字符,其中就使用到了责任链模式来完成特殊字符的替换。

写明了就是要用责任链模式,设计了一个通用的接口。

Chain

/**
 * 责任链接口
 * @author Looly
 *
 * @param <E> 元素类型
 * @param <T> 目标类类型,用于返回this对象
 */
public interface Chain<E, T> extends Iterable<E>{
	/**
	 * 加入责任链
	 * @param element 责任链新的环节元素
	 * @return this
	 */
	T addChain(E element);
}

ReplacerChain

/**
 * 字符串替换链,用于组合多个字符串替换逻辑
 * 
 * @author looly
 * @since 4.1.5
 */
public class ReplacerChain extends StrReplacer implements Chain<StrReplacer, ReplacerChain> {
	private static final long serialVersionUID = 1L;

	private final List<StrReplacer> replacers = new LinkedList<>();

	/**
	 * 构造
	 * 
	 * @param strReplacers 字符串替换器
	 */
	public ReplacerChain(StrReplacer... strReplacers) {
		for (StrReplacer strReplacer : strReplacers) {
			addChain(strReplacer);
		}
	}

	@SuppressWarnings("NullableProblems")
	@Override
	public Iterator<StrReplacer> iterator() {
		return replacers.iterator();
	}
	
	@Override
	public ReplacerChain addChain(StrReplacer element) {
		replacers.add(element);
		return this;
	}

	@Override
	protected int replace(CharSequence str, int pos, StrBuilder out) {
		int consumed = 0;
		for (StrReplacer strReplacer : replacers) {
			consumed = strReplacer.replace(str, pos, out);
			if (0 != consumed) {
				return consumed;
			}
		}
		return consumed;
	}

}

添加chain的逻辑放在构造方法中

public static String escapeHtml4(CharSequence html) {
	Html4Escape escape = new Html4Escape();
	return escape.replace(html).toString();
}
public Html4Escape() {
	addChain(new LookupReplacer(BASIC_ESCAPE));
	addChain(new LookupReplacer(ISO8859_1_ESCAPE));
	addChain(new LookupReplacer(HTML40_EXTENDED_ESCAPE));
}

责任链的写法有多种,可以使用一开始demo中的方式,省去HandlerChain,直接在每个Handler中设置后链的关系,也可以像Filter、Interceptor、Hutool那样定义一个集合,把所有的Handler都放入集合中,并让HandlerChain去维护,调用的时候只需要遍历集合去处理即可。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

码拉松

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值