代码精进之路-设计模式-拦截器链模式

f84c3149f48d4c86e9c6d797da0b4731.png

上一讲中说了《Tomcat的过滤器链 是如何实现的》,今天来看下 Spring的拦截器链是如何实现的。

从位置上来说 过滤器是属于Servlet容器级别的规范,拦截器是Spring自身的东西,一个请求 进入Tomcat,必先经过过滤器链,再调用Servlet方法,才能到达Spring,Spring MVC必须也得遵守Servlet规范才能被Tomcat调到。

在原生面向Servlet编程中(最早期通过Web.xml配置,后来使用Servlet注解配置),每个Servlet都是有一个url映射的,这样每个请求都会分配到指定的Servlet上进行处理。

而在Spring MVC 中 这种方式被打破,实际上接收Tomcat请求的只有一个Servlet,就是org.springframework.web.servlet.DispatcherServlet。

这个Servlet干的事情是进一步分发请求到我们编写的 Controller类(由此知道SpringMVC中的Controller类和Servlet完全没有关系,它就是普通的类 ,url映射等各种注解,都是Spring框架自身的东西,是为了DispatcherServlet分发时有据可依),那么在到达Controller之前和处理业务之后就可以做很多事情了,这些事情就由Spring自由发挥了,其中拦截器就是Spring的杰作之一。

21904be258b5a560f22ef0253bd7b4c4.png

使用Spring MVC 写个拦截器的demo,打个断点,观察下调用链

faa192264c6aef4a1e350fecc7f6d81b.png

dadb5954282397ea35dc9a125657a2ff.png

下面重点看下DispatcherServlet是如何与 HandlerExecutionChain配合完成拦截器的功能的

//DispatcherServlet中关键方法
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
   HttpServletRequest processedRequest = request;
   //出现了 HandlerExecutionChain
   HandlerExecutionChain mappedHandler = null;
   boolean multipartRequestParsed = false;


   WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);


   try {
      ModelAndView mv = null;
      Exception dispatchException = null;


      try {
         processedRequest = checkMultipart(request);
         multipartRequestParsed = (processedRequest != request);


         // Determine handler for the current request.
         // HandlerExecutionChain的初始化
         //(根据配置,每个请求需要经历的过滤器可以不同,里面还包括了对应的Controller信息)
         mappedHandler = getHandler(processedRequest);
         if (mappedHandler == null) {
            noHandlerFound(processedRequest, response);
            return;
         }


         // Determine handler adapter for the current request.
         HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());


         // Process last-modified header, if supported by the handler.
         String method = request.getMethod();
         boolean isGet = "GET".equals(method);
         if (isGet || "HEAD".equals(method)) {
            long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
            if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
               return;
            }
         }
         //执行过滤器前置方法
         if (!mappedHandler.applyPreHandle(processedRequest, response)) {
            return;
         }


         // Actually invoke the handler.
         //业务处理
         mv = ha.handle(processedRequest, response, mappedHandler.getHandler());


         if (asyncManager.isConcurrentHandlingStarted()) {
            return;
         }


         applyDefaultViewName(processedRequest, mv);
         //执行过滤器后置方法
         mappedHandler.applyPostHandle(processedRequest, response, mv);
      }
      catch (Exception ex) {
         dispatchException = ex;
      }
      catch (Throwable err) {
         // As of 4.3, we're processing Errors thrown from handler methods as well,
         // making them available for @ExceptionHandler methods and other scenarios.
         dispatchException = new NestedServletException("Handler dispatch failed", err);
      }
      processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
   }
   catch (Exception ex) {
      triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
   }
   catch (Throwable err) {
      triggerAfterCompletion(processedRequest, response, mappedHandler,
            new NestedServletException("Handler processing failed", err));
   }
   finally {
      if (asyncManager.isConcurrentHandlingStarted()) {
         // Instead of postHandle and afterCompletion
         if (mappedHandler != null) {
            mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
         }
      }
      else {
         // Clean up any resources used by a multipart request.
         if (multipartRequestParsed) {
            cleanupMultipart(processedRequest);
         }
      }
   }
}

关键地方

7dbfe2681473e60107b1600f5d4dbde2.png

看下HandlerExecutionChain的实现(只抽取了关键方法)

public class HandlerExecutionChain {


   boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
      HandlerInterceptor[] interceptors = getInterceptors();
      if (!ObjectUtils.isEmpty(interceptors)) {
          //通过for循环调用所有的拦截器
         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;
            }
            this.interceptorIndex = i;
         }
      }
      return true;
   }


   /**
    * Apply postHandle methods of registered interceptors.
    */
   void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv)
         throws Exception {


      HandlerInterceptor[] interceptors = getInterceptors();
      if (!ObjectUtils.isEmpty(interceptors)) {
           //通过for循环调用所有的拦截器,注意是倒序执行的
         for (int i = interceptors.length - 1; i >= 0; i--) {
            HandlerInterceptor interceptor = interceptors[i];
            interceptor.postHandle(request, response, this.handler, mv);
         }
      }
   }
   
   /**
 * Trigger afterCompletion callbacks on the mapped HandlerInterceptors.
 * Will just invoke afterCompletion for all interceptors whose preHandle invocation
 * has successfully completed and returned true.
 */
void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex)
      throws Exception {


   HandlerInterceptor[] interceptors = getInterceptors();
   //注意是倒序执行,并且只执行 preHandler成功的拦截器,interceptorIndex变量的作用体现出来了
   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);
         }
      }
   }
}


}

看完后发现只是循环调用了一下所有拦截器的方法(注意正序和倒序问题)下面老吕把这种实现方式 抽取出来 写个demo运行一下。

代码目录:

00fa3930ab842098cdb88de0a5690a00.png

/**
 * @Project fighting-core
 * @Description 请求分发器
 * @Author lvaolin
 * @Date 2021/12/26 下午2:25
 */
public class DispatcherServlet  {


   public void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {


      HandlerExecutionChain handlerExecutionChain = getHandlerExecutionChain();


      //应用前置处理器
      if (!handlerExecutionChain.applyPreHandle(request, response)) {
         return;
      }


      //处理业务
      String helloResponse = handlerExecutionChain.getHandler().hello(request.getName());


      //应用后置处理器
      handlerExecutionChain.applyPostHandle(request, response, null);


      System.out.println("响应到前端:'"+helloResponse+"'");


      handlerExecutionChain.triggerAfterCompletion(request,response,null);


   }


   private HandlerExecutionChain getHandlerExecutionChain() {
      HandlerExecutionChain handlerExecutionChain = new HandlerExecutionChain();
      handlerExecutionChain.setHandler(new MyController());
      handlerExecutionChain.addInterceptor(new MyInterceptor1());
      handlerExecutionChain.addInterceptor(new MyInterceptor2());
      handlerExecutionChain.addInterceptor(new MyInterceptor3());
      return handlerExecutionChain;
   }


}
/**
 * @Project fighting-core
 * @Description 拦截器链
 * @Author lvaolin
 * @Date 2021/12/26 下午2:25
 */
@Data
public class HandlerExecutionChain {


   //目标 Controller
   private MyController handler;


   /**
    * 拦截器集合
    */
   private HandlerInterceptor[] interceptors;


   /**
    * 拦截器集合 与 interceptors 相互转换
    */
   private List<HandlerInterceptor> interceptorList;


   //用来记录preHandler成功的拦截器个数的,在triggerAfterCompletion 方法中将要用到
   //因为triggerAfterCompletion只会触发preHandler执行成功的拦截器
   private int interceptorIndex = -1;




   public HandlerExecutionChain() {
   }


   public HandlerExecutionChain(MyController handler,  HandlerInterceptor... interceptors) {
      this.handler = handler;
      this.interceptors = interceptors;
   }




   public MyController getHandler() {
      return this.handler;
   }


   public void addInterceptor(HandlerInterceptor interceptor) {
      initInterceptorList().add(interceptor);
   }


   public void addInterceptor(int index, HandlerInterceptor interceptor) {
      initInterceptorList().add(index, interceptor);
   }


   public void addInterceptors(HandlerInterceptor... interceptors) {
      if (!ObjectUtils.isEmpty(interceptors)) {
         CollectionUtils.mergeArrayIntoCollection(interceptors, initInterceptorList());
      }
   }


   private List<HandlerInterceptor> initInterceptorList() {
      if (this.interceptorList == null) {
         this.interceptorList = new ArrayList<>();
         if (this.interceptors != null) {
            // An interceptor array specified through the constructor
            CollectionUtils.mergeArrayIntoCollection(this.interceptors, this.interceptorList);
         }
      }
      this.interceptors = null;
      return this.interceptorList;
   }


   /**
    * Return the array of interceptors to apply (in the given order).
    * @return the array of HandlerInterceptors instances (may be {@code null})
    */
   public HandlerInterceptor[] getInterceptors() {
      if (this.interceptors == null && this.interceptorList != null) {
         this.interceptors = this.interceptorList.toArray(new HandlerInterceptor[0]);
      }
      return this.interceptors;
   }




   /**
    * 执行过滤器集合的preHandle方法
    * Apply preHandle methods of registered interceptors.
    * @return {@code true} if the execution chain should proceed with the
    * next interceptor or the handler itself. Else, DispatcherServlet assumes
    * that this interceptor has already dealt with the response itself.
    */
   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;
            }
            //记录preHandler成功的最大下标
            this.interceptorIndex = i;
         }
      }
      return true;
   }


   /**
    * 执行过滤器集合的postHandle方法(倒序执行)
    * Apply postHandle methods of registered interceptors.
    */
   void applyPostHandle(HttpServletRequest request, HttpServletResponse response, ModelAndView mv)
         throws Exception {


      HandlerInterceptor[] interceptors = getInterceptors();
      if (!ObjectUtils.isEmpty(interceptors)) {
         //注意这个拦截器执行的先后顺序是和preHandle相反的,倒序执行
         for (int i = interceptors.length - 1; i >= 0; i--) {
            HandlerInterceptor interceptor = interceptors[i];
            interceptor.postHandle(request, response, this.handler, mv);
         }
      }
   }


   /**
    * 执行过滤器集合的afterCompletion方法(倒序执行)
    * Trigger afterCompletion callbacks on the mapped HandlerInterceptors.
    * Will just invoke afterCompletion for all interceptors whose preHandle invocation
    * has successfully completed and returned true.
    */
   void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response,  Exception ex)
         throws Exception {


      HandlerInterceptor[] interceptors = getInterceptors();
      if (!ObjectUtils.isEmpty(interceptors)) {
         //注意这个拦截器执行的先后顺序是和preHandle相反的,并且只执行 preHandle成功的过滤器
         for (int i = this.interceptorIndex; i >= 0; i--) {
            HandlerInterceptor interceptor = interceptors[i];
            try {
               interceptor.afterCompletion(request, response, this.handler, ex);
            }
            catch (Throwable ex2) {
               ex2.printStackTrace();
            }
         }
      }
   }






}
/**
 * @Project fighting-core
 * @Description 模拟Spring Controller
 * @Author lvaolin
 * @Date 2021/12/26 下午2:25
 */
public class MyController {


    @RequestMapping("/hello")
    String hello(String name){
        System.out.println("-业务处理开始-");
        System.out.println("-业务处理结束-");
        return "hello "+name;
    }
}


/**
 * @Project fighting-core
 * @Description 我的拦截器
 * @Author lvaolin
 * @Date 2021/12/26 下午2:25
 */
public class MyInterceptor1 implements HandlerInterceptor {


    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("preHandle分布式日志追踪");
        return true;
    }


    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("postHandle分布式日志追踪后置处理");
    }


    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("afterCompletion分布式日志追踪结束后处理");
    }


}


public class MyInterceptor2 implements HandlerInterceptor {


    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if ("大黄鸭".equals(request.getName())) {
            System.out.println("preHandle登录鉴权成功:"+request.getName());
            return true;
        }else{
            System.out.println("preHandle登录鉴权失败:"+request.getName());
            return false;
        }


    }


    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("postHandle登录鉴权后置处理");
    }


    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("afterCompletion登录鉴权结束后处理");
    }


}


public class MyInterceptor3 implements HandlerInterceptor {


    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("preHandle购买验证");
        return true;
    }


    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("postHandle购买验证后置处理");
    }


    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("afterCompletion购买验证结束后处理");
    }


}
/**
 * @Project fighting-core
 * @Description 上下文模拟
 * @Author lvaolin
 * @Date 2021/12/26 下午2:28
 */
public class Main {


    public static void main(String[] args) {


        HttpServletRequest httpServletRequest = new HttpServletRequest();
        HttpServletResponse httpServletResponse = new HttpServletResponse();


        DispatcherServlet dispatcherServlet = new DispatcherServlet();
        //拦截验证成功的例子
        try {
            httpServletRequest.setName("大黄鸭");
            dispatcherServlet.doDispatch(httpServletRequest,httpServletResponse);
        } catch (Exception e) {
            e.printStackTrace();
        }


        System.out.println("--------------------------------------------");


        //拦截验证失败的例子
        try {
            httpServletRequest.setName("派大星");
            dispatcherServlet.doDispatch(httpServletRequest,httpServletResponse);


        } catch (Exception e) {
            e.printStackTrace();
        }




    }
}

运行效果

fc344930a78dd2d7efd0edd9dd087e72.png

可以看到拦截器模式实现起来比较简单,但是相当实用,我们平时开发中可以多考虑下这个模式。

代码参考老吕的github:

https://github.com/lvaolin/fighting-base/tree/main/src/main/java/com/dhy/designpatterns/chainOfResponsibility/interceptor

622f5c4eb4a65830e44866a52dfaec4d.png

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

吕哥架构

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

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

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

打赏作者

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

抵扣说明:

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

余额充值