上一讲中说了《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 ListinterceptorList; //用来记录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
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)