日常生活中办理一件事情往往需要一系列的流程,且流程直接还是有顺序的。比如我们去医院看病,首先要挂号,然后再去排号会诊,医生根据病人的情况开具指定的药品,接口病人根据医生开具的药品单去缴费,缴费完成后再去取药窗口排队取药。这一系列的环节都是有顺序,且前后流程直接都是环环相扣的,不能说先完成某项再来处理前面未完成的。
软件开发的过程中,也经常会用到此种场景。对于一个对象经过不同的处理器(过滤器)去处理,这里其实就运用到了今天所要讲的设计模式责任链模式。
责任链也叫职责链,将请求的发送和接收解耦,让多个接收对象都有机会处理这个请求。将这些接收对象串成一条链,并沿着这条链传递这个请求,直到链上的某个接收对象能够处理它为止。
案例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);
}
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);
}
}
}
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);
}
}
}
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;
}
}
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天经理审批审批
可以看到得到的结果是相同的,实现细节有些变化,客户端(使用方)通过集合遍历执行的方式来执行处理者,根据处理者的结果觉得是都需要向下继续执行,这比第一种实现方式的好处就是可以无需关注,处理者之间设置下一处理者。而是通过添加到集合的顺序来决定执行顺序。
1.优点:
2.缺点:
责任链在源码中的使用场景还是很多的,我们常见的servlet filter
过滤器,或者spring mvc中的Interceptor
拦截器。
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>DemoFilterfilter-name>
<filter-class>com.edu.DemoFilterfilter-class> filter>
<filter-mapping>
<filter-name>DemoFilterfilter-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;
}}
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: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()函数,用来实现拦截的功能。
✨✨ 欢迎订阅个人的微信公众号 享及时博文更新