前言:这两天学习了代理模式,自然想到了 springmvc 的 aop 使用的就是动态代理,拦截器使用的就是 jdk 的动态代理。今天看了看源码,记录一下。转载请注明出处:https://www.cnblogs.com/yuxiaole/p/9969360.html

springMVC 的拦截器使用移步:Java Servlet 过滤器与 springmvc 拦截器的区别?

源码解析

  springMVC 的所有连接入口都会进入 DispatcherServlet,然后在这里面去调用真正的 Controller。而拦截器要达到的作用则是在调用 Controller 前后去做一些事情。所以现在需要看看 DispatcherServlet 的源码。

DispatcherServlet

  DispatcherServlet 的源码入口在 doService() 方法。

  1. protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
  2. if(this.logger.isDebugEnabled()) {
  3. String resumed = WebAsyncUtils.getAsyncManager(request).hasConcurrentResult()?" resumed":"";
  4. this.logger.debug("DispatcherServlet with name '" + this.getServletName() + "'" + resumed + " processing " + request.getMethod() + " request for [" + getRequestUri(request) + "]");
  5. }
  6.  
  7. Map<String, Object> attributesSnapshot = null;
  8. if(WebUtils.isIncludeRequest(request)) {
  9. attributesSnapshot = new HashMap();
  10. Enumeration attrNames = request.getAttributeNames();
  11.  
  12. label108:
  13. while(true) {
  14. String attrName;
  15. do {
  16. if(!attrNames.hasMoreElements()) {
  17. break label108;
  18. }
  19.  
  20. attrName = (String)attrNames.nextElement();
  21. } while(!this.cleanupAfterInclude && !attrName.startsWith("org.springframework.web.servlet"));
  22.  
  23. attributesSnapshot.put(attrName, request.getAttribute(attrName));
  24. }
  25. }
  26.  
  27. request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.getWebApplicationContext());
  28. request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
  29. request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
  30. request.setAttribute(THEME_SOURCE_ATTRIBUTE, this.getThemeSource());
  31. FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
  32. if(inputFlashMap != null) {
  33. request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
  34. }
  35.  
  36. request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
  37. request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);
  38.  
  39. try {
  40. this.doDispatch(request, response);
  41. } finally {
  42. if(!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted() && attributesSnapshot != null) {
  43. this.restoreAttributesAfterInclude(request, attributesSnapshot);
  44. }
  45.  
  46. }
  47.  
  48. }

  doService() 方法里面处理了一些信息之后,调用了 doDispatch() 方法。

  1. protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
  2. HttpServletRequest processedRequest = request;
  3. HandlerExecutionChain mappedHandler = null;
  4. boolean multipartRequestParsed = false;
  5. WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
  6.  
  7. try {
  8. try {
  9. ModelAndView mv = null;
  10. Object dispatchException = null;
  11.  
  12. try {
  13. processedRequest = this.checkMultipart(request);
  14. multipartRequestParsed = processedRequest != request;
  15. mappedHandler = this.getHandler(processedRequest);
  16. if(mappedHandler == null || mappedHandler.getHandler() == null) {
  17. this.noHandlerFound(processedRequest, response);
  18. return;
  19. }
  20.  
  21. HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
  22. String method = request.getMethod();
  23. boolean isGet = "GET".equals(method);
  24. if(isGet || "HEAD".equals(method)) {
  25. long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
  26. if(this.logger.isDebugEnabled()) {
  27. this.logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
  28. }
  29.  
  30. if((new ServletWebRequest(request, response)).checkNotModified(lastModified) && isGet) {
  31. return;
  32. }
  33. }
  34.  
  35. if(!mappedHandler.applyPreHandle(processedRequest, response)) {
  36. return;
  37. }
  38.  
  39. mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
  40. if(asyncManager.isConcurrentHandlingStarted()) {
  41. return;
  42. }
  43.  
  44. this.applyDefaultViewName(processedRequest, mv);
  45. mappedHandler.applyPostHandle(processedRequest, response, mv);
  46. } catch (Exception var20) {
  47. dispatchException = var20;
  48. } catch (Throwable var21) {
  49. dispatchException = new NestedServletException("Handler dispatch failed", var21);
  50. }
  51.  
  52. this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);
  53. } catch (Exception var22) {
  54. this.triggerAfterCompletion(processedRequest, response, mappedHandler, var22);
  55. } catch (Throwable var23) {
  56. this.triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", var23));
  57. }
  58.  
  59. } finally {
  60. if(asyncManager.isConcurrentHandlingStarted()) {
  61. if(mappedHandler != null) {
  62. mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
  63. }
  64. } else if(multipartRequestParsed) {
  65. this.cleanupMultipart(processedRequest);
  66. }
  67.  
  68. }
  69. }

  doDispatch() 方法中通过 this.getHandler(processedRequest); 获取到了处理器链 HandlerExecutionChain mappedHandle 之后,去执行了  mappedHandler.applyPreHandle(processedRequest, response) 方法,从而执行了拦截器的 preHandle 方法,如果返回为 false,则会调用处理器链的 triggerAfterCompletion 方法,然后 DispatchServlet 类中会直接 retreturn;如果返回为 true,则继续。

  然后调用了 HandlerAdapter 的 handler() 方法;

  然后调用了 mappedHandler.applyPostHandle(processedRequest, response, mv); 方法,从而执行了拦截器的 postHandle 方法;

  然后调用了 this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException); 方法;在 processDispatchResult() 方法里面渲染了对应的视图,然后调用了 mappedHandler.triggerAfterCompletion(request, response, (Exception)null); 方法,从而执行了拦截器的 afterCompletion 方法。

  processDispatchResult() 源码如下:

  1. private void processDispatchResult(HttpServletRequest request, HttpServletResponse response, HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception {
  2. boolean errorView = false;
  3. if(exception != null) {
  4. if(exception instanceof ModelAndViewDefiningException) {
  5. this.logger.debug("ModelAndViewDefiningException encountered", exception);
  6. mv = ((ModelAndViewDefiningException)exception).getModelAndView();
  7. } else {
  8. Object handler = mappedHandler != null?mappedHandler.getHandler():null;
  9. mv = this.processHandlerException(request, response, handler, exception);
  10. errorView = mv != null;
  11. }
  12. }
  13.  
  14. if(mv != null && !mv.wasCleared()) {
  15. this.render(mv, request, response);
  16. if(errorView) {
  17. WebUtils.clearErrorRequestAttributes(request);
  18. }
  19. } else if(this.logger.isDebugEnabled()) {
  20. this.logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + this.getServletName() + "': assuming HandlerAdapter completed request handling");
  21. }
  22.  
  23. if(!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
  24. if(mappedHandler != null) {
  25. mappedHandler.triggerAfterCompletion(request, response, (Exception)null);
  26. }
  27.  
  28. }
  29. }

  从 doDispatch() 方法这里主要需要知道 HandlerExecutionChain mappedHandler = this.getHandler(processedRequest); 这一句调用的 getHandler() 方法,源码如下。

  getHandle() 方法里是通过 HandlerMapping 的 getHandler 方法返回 HandlerExecutionChain 的。

  从代码中不难看出整个逻辑就是依次判断 servlet 中的每个 handlerMapping 是否能够匹配该请求,直到找到那个匹配的然后返回处理结果。

  1. protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
  2. Iterator var2 = this.handlerMappings.iterator();
  3.  
  4. HandlerExecutionChain handler;
  5. do {
  6. if(!var2.hasNext()) {
  7. return null;
  8. }
  9.  
  10. HandlerMapping hm = (HandlerMapping)var2.next();
  11. if(this.logger.isTraceEnabled()) {
  12. this.logger.trace("Testing handler map [" + hm + "] in DispatcherServlet with name '" + this.getServletName() + "'");
  13. }
  14.  
  15. handler = hm.getHandler(request);
  16. } while(handler == null);
  17.  
  18. return handler;
  19. }

HandlerExecutionChain 类

  applyPreHandle() 方法源码如下:可以看出是顺序调用拦截器的 preHandle() 方法。preHandle 返回 false 时调用了 triggerAfterCompletion 方法。

  1. boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
  2. HandlerInterceptor[] interceptors = this.getInterceptors();
  3. if(!ObjectUtils.isEmpty(interceptors)) {
  4. for(int i = 0; i < interceptors.length; this.interceptorIndex = i++) {
  5. HandlerInterceptor interceptor = interceptors[i];
  6. if(!interceptor.preHandle(request, response, this.handler)) {
  7. this.triggerAfterCompletion(request, response, (Exception)null);
  8. return false;
  9. }
  10. }
  11. }
  12.  
  13. return true;
  14. }

  applyPostHandle() 方法源码如下:可以看出这里是逆序调用 postHandle 方法。

  1. void applyPostHandle(HttpServletRequest request, HttpServletResponse response, ModelAndView mv) throws Exception {
  2. HandlerInterceptor[] interceptors = this.getInterceptors();
  3. if(!ObjectUtils.isEmpty(interceptors)) {
  4. for(int i = interceptors.length - 1; i >= 0; --i) {
  5. HandlerInterceptor interceptor = interceptors[i];
  6. interceptor.postHandle(request, response, this.handler, mv);
  7. }
  8. }
  9.  
  10. }

 triggerAfterCompletion() 方法源码如下:可以看出这里也是逆序调用 afterCompletion 方法。

  1. void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, Exception ex) throws Exception {
  2. HandlerInterceptor[] interceptors = this.getInterceptors();
  3. if(!ObjectUtils.isEmpty(interceptors)) {
  4. for(int i = this.interceptorIndex; i >= 0; --i) {
  5. HandlerInterceptor interceptor = interceptors[i];
  6.  
  7. try {
  8. interceptor.afterCompletion(request, response, this.handler, ex);
  9. } catch (Throwable var8) {
  10. logger.error("HandlerInterceptor.afterCompletion threw exception", var8);
  11. }
  12. }
  13. }
  14.  
  15. }

  

转载请注明出处:https://www.cnblogs.com/yuxiaole/p/9969360.html

springMVC 拦截器源码解析的更多相关文章

  1. Struts2 源码分析-----拦截器源码解析 --- ParametersInterceptor

    ParametersInterceptor拦截器其主要功能是把ActionContext中的请求参数设置到ValueStack中,如果栈顶是当前Action则把请求参数设置到了Action中,如果栈顶 ...

  2. struts2拦截器源码分析

    前面博客我们介绍了开发struts2应用程序的基本流程(开发一个struts2的实例),通过前面我们知道了struts2实现请求转发和配置文件加载都是拦截器进行的操作,这也就是为什么我们要在web.x ...

  3. Jfinal拦截器源码解读

    本文对Jfinal拦截器源码做以下分析说明

  4. 0002 - Spring MVC 拦截器源码简析:拦截器加载与执行

    1.概述 Spring MVC中的拦截器(Interceptor)类似于Servlet中的过滤器(Filter),它主要用于拦截用户请求并作相应的处理.例如通过拦截器可以进行权限验证.记录请求信息的日 ...

  5. RestFramework之序列化器源码解析

    一.源码解析之序列化: 1.当视图类进行实例化序列化类做了如下操作: #ModelSerializer继承Serializer再继承BaseSerializer(此类定义实例化方法) #在BaseSe ...

  6. DRF之解析器源码解析

    解析器 RESTful一种API的命名风格,主要因为前后端分离开发出现前后端分离: 用户访问静态文件的服务器,数据全部由ajax请求给到 解析器的作用就是服务端接收客户端传过来的数据,把数据解析成自己 ...

  7. 复习整理9:SpringMVC应用以及源码解析

    一:SpringMVC是什么 SpringMVC只是Spring的一个子框架,作用学过Struts2的应该很好理解,他们都是MVC的框架.学他就是用来代替Struts2的,那么为什么不用Struts2 ...

  8. OkHttp3 拦截器源码分析

    OkHttp 拦截器流程源码分析 在这篇博客 OkHttp3 拦截器(Interceptor) ,我们已经介绍了拦截器的作用,拦截器是 OkHttp 提供的对 Http 请求和响应进行统一处理的强大机 ...

  9. 关于阅读Struts2部分拦截器源码的记录

    Struts2中的拦截器在ActionInvocation对象的invoke()方法中执行. ActionInvocation对象从配置文件中读取Interceptor对象,加入到自己的存取拦截器的容 ...

随机推荐

  1. 深入理解Java虚拟机二之Java内存区域与内存溢出异常

    运行时数据区域 1.线程独有的内存区域 PROGRAM COUNTER REGISTER 程序计数器 程序计数器空间较小,是当前线程执行字节码的行号指示器,字节码解释器工作时就是通过改变这个计数器的值 ...

  2. hiho 第六周 01背包

    简单的01背包,没有报名,这周的没有权限提交 #include<iostream> #include<memory.h> using namespace std; #defin ...

  3. Android框架式编程之MVP架构

    MVP(Model-View-Presenter)模式.是将APP的结构分为三层:View - Presenter - Model. View 1. 提供UI交互 2. 在presenter的控制下修 ...

  4. i==1 && resolve()

    for( var i=100000 ; i>0 ; i-- ){ i==1 && resolve() } var dd = 988889;console.log(`${dd}`) ...

  5. Java学习笔记37(字节流)

    输出:程序到文件 输入:文件到程序 字节输出流:OutputStream类 作用:在java程序中写文件 这个类是抽象类,必须使用它的子类 方法: 写入: package demo; import j ...

  6. XStream进行xml和bean互转

    加入pom <dependency> <groupId>com.thoughtworks.xstream</groupId> <artifactId>x ...

  7. Liferay7 BPM门户开发之8: Activiti实用问题集合

    1.如何实现审核的上级获取(任务逐级审批) 这个是必备功能,通过Spring的注入+Activiti表达式可以很容易解决. 可参考: http://blog.csdn.net/sunxing007/a ...

  8. postgresql-磁盘空间不足问题排查

    问题背景 加压测试过程中发现插入数据过程中报错:could not write to hash-join temporary file: 设备上没有空间.但是查看服务器还有很多空闲空间,是什么导致这样 ...

  9. TypeEncodings

    官网链接: https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Ar ...

  10. web自动化测试(java)---测试过程中遇到的错误合集

    摸索测试,不管是安装.调测第一个用例都会遇到各种各样的问题,或是自己的问题或是程序本身设置问题 只有把所有问题记录下来,才对得起自己的经历 1.设置firefox的执行文件错误 Exception i ...