日常生活中办理一件事情往往需要一系列的流程,且流程直接还是有顺序的。比如我们去医院看病,首先要挂号,然后再去排号会诊,医生根据病人的情况开具指定的药品,接口病人根据医生开具的药品单去缴费,缴费完成后再去取药窗口排队取药。这一系列的环节都是有顺序,且前后流程直接都是环环相扣的,不能说先完成某项再来处理前面未完成的。
软件开发的过程中,也经常会用到此种场景。对于一个对象经过不同的处理器(过滤器)去处理,这里其实就运用到了今天所要讲的设计模式责任链模式。
定义
责任链也叫职责链,将请求的发送和接收解耦,让多个接收对象都有机会处理这个请求。将这些接收对象串成一条链,并沿着这条链传递这个请求,直到链上的某个接收对象能够处理它为止。
责任链模式的结构与实现
- 模式的结构 职责链模式主要包含以下角色。
- 抽象处理者(Handler)角色:定义一个处理请求的接口,包含抽象处理方法和一个后继连接。
- 具体处理者(Concrete Handler)角色:实现抽象处理者的处理方法,判断能否处理本次请求,如果可以处理请求则处理,否则将该请求转给它的后继者。
- 客户类(Client)角色:创建处理链,并向链头的具体处理者对象提交请求,它不关心处理细节和请求的传递过程。
2.责任链模式结构图
代码案例
1.案例1
案例1举一个工作中经常遇到的场景,上班中不可避免的会遇到一些事情,需要请假。公司的行政部往往会根据个人的请假天数,去设置不同级别的领导审批。这里比如我们请假小于等于1天主管审批、大于1小于等于3天主管审批,大于3天Boos审批。这里将每个角色定义为责任链模式中的具体处理者,具体看代码实现。
- 抽象处理者
//抽象执行处理者
public abstract class HandlerChain {
protected HandlerChain handlerChain;
public HandlerChain getHandlerChain() {
return handlerChain;
}
public void setHandlerChain(HandlerChain handlerChain) {
this.handlerChain = handlerChain;
}
//执行逻辑
protected abstract void chain(Integer request);
}
- 具体处理者1 主管
public class DirectorApproveHandler extends HandlerChain{
@Override
protected void chain(Integer request) {
if (Objects.isNull(request)) {
return;
}
if (request > 0 && request <=1) {
System.out.println("请假小于一天主管审批");
} else {
handlerChain.chain(request);
}
}
}
- 具体处理者2 经理
public class ManagerApproveHandler extends HandlerChain{
@Override
protected void chain(Integer request) {
if (Objects.isNull(request)) {
return;
}
if (request > 1 && request <=3) {
System.out.println("请假1~3天经理审批审批");
} else {
handlerChain.chain(request);
}
}
}
- 具体处理者3 boos
public class BossApproveHandler extends HandlerChain{
@Override
protected void chain(Integer request) {
if (Objects.isNull(request)) {
return;
}
if (request > 3) {
System.out.println("请假大于3天Boos审批");
} else {
handlerChain.chain(request);
}
}
}
- 客户端
public class Client {
public static void main(String[] args) {
HandlerChain directorHandlerChain = new DirectorApproveHandler();
HandlerChain managerHandlerChain = new ManagerApproveHandler();
HandlerChain bossHandlerChain = new BossApproveHandler();
//设置责任链执行
directorHandlerChain.setHandlerChain(managerHandlerChain);
managerHandlerChain.setHandlerChain(bossHandlerChain);
bossHandlerChain.setHandlerChain(directorHandlerChain);
//调用
directorHandlerChain.chain(2);
}
}
客户端将需要设置的具体处理者设置为处理链,然后从最初始的节点开始调用,满足的节点则处理。
请假1~3天经理审批审批
责任链模式上面的案例只是一种使用场景,在执行当前节点时将每个处理节点的下一节点也设置完成。如果满足相当于不会再往下执行,我们如果再某两个处理器之间再添加一个处理器,除了增加的处理器设置,还要更改上一个处理器的节点设置,不利于拓展。
下面介绍一下另外一种变种,通过集合的方式维护一批处理器,且处理器按照我们预定的顺序添加即可,下面通过源码的方式给出;
- 抽象处理者
//抽象执行处理者
public abstract class HandlerChain {
//执行逻辑
protected abstract boolean chain(Integer request);
}
- 具体处理者 主管
//主管审批
public class DirectorApproveHandler extends HandlerChain{
@Override
protected boolean chain(Integer request) {
if (Objects.isNull(request)) {
return false;
}
if (request > 0 && request <=1) {
System.out.println("请假小于一天主管审批");
return false;
}
return true;
}
}
- 具体处理者 经理
public class ManagerApproveHandler extends HandlerChain{
@Override
protected boolean chain(Integer request) {
if (Objects.isNull(request)) {
return false;
}
if (request > 1 && request <=3) {
System.out.println("请假1~3天经理审批审批");
return false;
}
return true;
}
}
- 具体处理者 boos
public class BossApproveHandler extends HandlerChain{
@Override
protected boolean chain(Integer request) {
if (Objects.isNull(request)) {
return false;
}
if (request > 3) {
System.out.println("请假大于3天Boos审批");
return false;
}
return true;
}
}
- 客户端
public class Client {
public static void main(String[] args) {
HandlerChain directorHandlerChain = new DirectorApproveHandler();
HandlerChain managerHandlerChain = new ManagerApproveHandler();
HandlerChain bossHandlerChain = new BossApproveHandler();
List<HandlerChain> list = new ArrayList<>(3);
list.addAll(Arrays.asList(directorHandlerChain,managerHandlerChain,bossHandlerChain));
Integer request = 2;
for (HandlerChain handlerChain : list) {
if (!handlerChain.chain(request)) {
break;
}
}
}
}
请假1~3天经理审批审批
案例2结构图
可以看到得到的结果是相同的,实现细节有些变化,客户端(使用方)通过集合遍历执行的方式来执行处理者,根据处理者的结果觉得是都需要向下继续执行,这比第一种实现方式的好处就是可以无需关注,处理者之间设置下一处理者。而是通过添加到集合的顺序来决定执行顺序。
责任链模式的优缺点及应用场景
1.优点:
- 降低了对象之间的耦合度。该模式使得一个对象无须知道到底是哪一个对象处理其请求以及链的结构,发送者和接收者也无须拥有对方的明确信息。
- 增强了系统的可扩展性。可以根据需要增加新的请求处理类,满足开闭原则。
-增强了给对象指派职责的灵活性。当工作流程发生变化,可以动态地改变链内的成员或者调动它们的次序,也可动态地新增或者删除责任。 - 责任链简化了对象之间的连接。每个对象只需保持一个指向其后继者的引用,不需保持其他所有处理者的引用,这避免了使用众多的 if 或者 if···else 语句。
- 责任分担。每个类只需要处理自己该处理的工作,不该处理的传递给下一个对象完成,明确各类的责任范围,符合类的单一职责原则。
2.缺点:
- 不能保证每个请求一定被处理。由于一个请求没有明确的接收者,所以不能保证它一定会被处理,该请求可能一直传到链的末端都得不到处理。
- 对比较长的职责链,请求的处理可能涉及多个处理对象,系统性能将受到一定影响。
责任链模式的应用场景
- 一个全局上下文对象贯穿整条处理链路,对个处理器可以抽象为更单一职责的处理器
- 可动态指定一组对象处理请求,或添加新的处理者。
需要在不明确指定请求处理者的情况下,向多个处理者中的一个提交请求。
责任链模式在源码中的使用
责任链在源码中的使用场景还是很多的,我们常见的servlet filter
过滤器,或者spring mvc中的Interceptor
拦截器。
1.Servlet中的过滤器Filter
Servlet Filter是Java Servlet规范中定义的组件,翻译成中文就是过滤器,它可以实现对HTTP请求的过滤功 能,比如鉴权、限流、记录日志、验证参数等等。因为它是Servlet规范的一部分,所以,只要是支持 Servlet的Web容器(比如,Tomcat、Jetty等)
为了方便理解,画一张简易的示意图。
如果做过web开发同学应该都很清楚,使用也很简单,如果我们要添加一个过滤器,只需要实现Filter接口
Filter接口
public interface Filter {
default void init(FilterConfig filterConfig) throws ServletException {
}
void doFilter(ServletRequest var1, ServletResponse var2, FilterChain var3) throws IOException, ServletException;
default void destroy() {
}
}
例.
public class DemoFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
// 在创建Filter时自动调用,其中filterConfig包含这个Filter的配置参数,比如name之类的(从配置文件中读取的) }
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("拦截客戶端发送来的请求.");
filterChain.doFilter(servletRequest, servletResponse);
System.out.println("拦截发送给客戶端的响应.");
}
@Override
public void destroy() {
// 在销毁Filter时自动调用
}
}
web.xml中配置添加的过滤器
// 在web.xml配置文件中如下配置:
<filter>
<filter-name>DemoFilter</filter-name>
<filter-class>com.edu.DemoFilter</filter-class> </filter>
<filter-mapping>
<filter-name>DemoFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
当发起http请求时候,自定义拦截器就会起到相应的作用。而且可以发现添加一个过滤器非常方便,无需过多的配置改动。满足我们的设计原则开原则。
那么Servlet是如何实现责任链模式的,责任链模式的实现包含处理器接口(IHandler)或抽象类(Handler),以及处理器链(HandlerChain)。对应到Servlet Filter,javax.servlet.Filter就是处理器接口,FilterChain就是处 理器链。接下来,我们重点来看FilterChain是如何实现的。
前面讲到Servlet只是一个规范,并不包含具体的实现,所以,Servlet中的FilterChain只是一个接口定义。具体的实现类由组从Servlet规范的Web容器来提供,比如,ApplicationFilterChain 类就是Tomcat提供的FilterChain的实现类,源码如下所示。
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 + INCREMENT];
System.arraycopy(filters, 0, newFilters, 0, n);
filters = newFilters;
}
filters[n++] = filterConfig;
}}
2.SpringMvc中的拦截器Interceptor
Spring Interceptor,翻译成中文 就是拦截器。尽管英文单词和中文翻译都不同,但这两者基本上可以看作一个概念,都用来实现对HTTP请 求进行拦截处理。
与Servlet Filter它们不同之处在于,Servlet Filter是Servlet规范的一部分,实现依赖于Web容器。Spring Interceptor是 Spring MVC框架的一部分,由Spring MVC框架来提供实现。客戶端发送的请求,会先经过Servlet Filter, 然后再经过Spring Interceptor,最后到达具体的业务代码中。
这里也用一张简单的示意图,展示一张拦截器的执行流程
使用,与过滤器类似我们只需要实现HandlerInterceptor
接口
例.
public class DemoInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("拦截客戶端发送来的请求.");
return true;//ture 继续往下执行
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("拦截发给客户的请求");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("这里总是被执行");
}
}
配置
//在Spring MVC配置文件中配置interceptors <mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/*"/>
<bean class="com.edu.DemoInterceptor" />
</mvc:interceptor>
</mvc:interceptors>
拦截器也是基于职责链模式实现的。其中,HandlerExecutionChain类是职责链模式中的处理器链。它的 实现相较于Tomcat中的ApplicationFilterChain来说,逻辑更加清晰,不需要使用递归来实现,主要是因为 它将请求和响应的拦截工作,拆分到了两个函数中实现。HandlerExecutionChain的源码如下所示。
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 {
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, ModelAndView mv) throws Exception { 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, Exception ex) 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);
}
} }
}
在Spring框架中,DispatcherServlet的doDispatch()方法来分发请求,它在真正的业务逻辑执行前后,执 行HandlerExecutionChain中的applyPreHandle()和applyPostHandle()函数,用来实现拦截的功能。