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

代码精进之路-设计模式-拦截器链模式,第1张

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

上一讲中说了《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的杰作之一。

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

下面重点看下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);
         }
      }
   }
}

关键地方

看下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;
   }


   
   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);
         }
      }
   }
   
   
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运行一下。

代码目录:

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;
   }


}
@Data
public class HandlerExecutionChain {


   //目标 Controller
   private MyController handler;


   
   private HandlerInterceptor[] interceptors;


   
   private List 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 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;
   }


   
   public HandlerInterceptor[] getInterceptors() {
      if (this.interceptors == null && this.interceptorList != null) {
         this.interceptors = this.interceptorList.toArray(new HandlerInterceptor[0]);
      }
      return this.interceptors;
   }




   
   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;
   }


   
   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);
         }
      }
   }


   
   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();
            }
         }
      }
   }






}
public class MyController {


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



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购买验证结束后处理");
    }


}
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();
        }




    }
}

运行效果

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

代码参考老吕的github:

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

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5684395.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-17
下一篇 2022-12-17

发表评论

登录后才能评论

评论列表(0条)

保存