SpringMVC—DispatcherServlet解析[通俗易懂]

SpringMVC—DispatcherServlet解析[通俗易懂]DispatcherServlet其实是Servlet接口的实现类,它的本质其实就是一个Servlet。DispatcherServlet继承图

大家好,欢迎来到IT知识分享网。

DispatcherServlet其实是Servlet接口的实现类,它的本质其实就是一个Servlet。

SpringMVC—DispatcherServlet解析[通俗易懂]

DispatcherServlet继承图

在讲DispatcherServlet之前,首先介绍一下Servlet的生命周期。

Servlet的生命周期

1. init : 初始化

当Servlet第一次被请求时,服务器就会调用init()方法初始化一个Servlet对象,但是这个方法在后续的请求中不会再次被调用,仅仅只会调用一次。在调用这个方法时,Servlet容器会传入一个ServletConfig对象来对Servlet对象进行初始化。

当发送http请求时:StandardWrapper类会调用allocate()方法给Servlet分配一个初始化的实例。

private synchronized void initServlet(Servlet servlet)
            throws ServletException {
         // 如果servlet已被初始化,则return
        if (instanceInitialized && !singleThreadModel) return;

        // Call the initialization method of this servlet
        try {
            if( Globals.IS_SECURITY_ENABLED) {
                boolean success = false;
                try {
                    Object[] args = new Object[] { facade };
                    SecurityUtil.doAsPrivilege("init",
                                               servlet,
                                               classType,
                                               args);
                    success = true;
                } finally {
                    if (!success) {
                        // destroy() will not be called, thus clear the reference now
                        SecurityUtil.remove(servlet);
                    }
                }
            } else {
              // 调用init()方法初始化,facade: 是ServletConfig的实现类
              // 这一步会转到DispatcherServlet初始化的过程
                servlet.init(facade);
            }

            instanceInitialized = true;
        } catch (UnavailableException f) {
            unavailable(f);
            throw f;
        } catch (ServletException f) {
            // If the servlet wanted to be unavailable it would have
            // said so, so do not call unavailable(null).
            throw f;
        } catch (Throwable f) {
            ExceptionUtils.handleThrowable(f);
            getServletContext().log(sm.getString("standardWrapper.initException", getName()), f);
            // If the servlet wanted to be unavailable it would have
            // said so, so do not call unavailable(null).
            throw new ServletException
                (sm.getString("standardWrapper.initException", getName()), f);
        }
    }

IT知识分享网

2. service

每次客户端请求这个Servlet时,Servlet容器就会调用service()方法处理请求;在后续的请求中,Servlet容器就只会调用这个方法了。

3. destroy

当服务器关闭或者停止时,Servlet容器就会调用destroy()销毁这个Servlet。

DispatcherServlet初始化

由于DispatcherServlet本质就是一个Servlet,它对Servlet进行了封装,它的生命周期与Servlet一致,当调用init(ServletConfig servletConfig)方法初始化的时候,这个时候采用Java面向对象思想:当子类继承父类时,如果子类没有重写父类的方法,则首先调用父类的方法,它的父类给他做了很多初始化的工作。

由于DispatcherServlet没有重写init(ServletConfig servletConfig)方法,它会找到父类的init(ServletConfig servletConfig)方法,然后调用。

SpringMVC—DispatcherServlet解析[通俗易懂]

请求流

1. 在其父类GenericServlet类中重写了init(ServletConfig servletConfig)方法

SpringMVC—DispatcherServlet解析[通俗易懂]

2. 从第一步可以看出DispatcherServlet对象会调用init()初始化方法,由于DispatcherServlet类没有重写init()方法,它会调用父类的init()方法,观察继承结构可知,只有HttpServletBean类重写了init()方法

IT知识分享网    @Override
    public final void init() throws ServletException {

        // ServletConfigPropertyValues 是静态内部类,使用 ServletConfig 获取 web.xml 中配置的参数
        PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
        if (!pvs.isEmpty()) {
            try {
                // 使用 BeanWrapper 来构造 DispatcherServlet
                BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
                ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
                bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
                // 空实现
                initBeanWrapper(bw);
                bw.setPropertyValues(pvs, true);
            } catch (BeansException ex) {
                if (logger.isErrorEnabled()) {
                    logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
                }
                throw ex;
            }
        }

        // 让子类实现的方法,这种在父类定义在子类实现的方式叫做模版方法模式
        initServletBean();
    }

3. 从上一步看到,它还要调用initServletBean方法,这个方法在DispatcherServlet类也没有重写,那只能往DispatcherServlet类的父类,HttpServletBean的子类找了,显而易见,只有FrameworkServlet类满足条件

initServletBean():建立 WebApplicationContext 容器(有时也称上下文),并加载 SpringMVC 配置文件中定义的 Bean 到该容器中,最后将该容器添加到 ServletContext 中。

protected WebApplicationContext initWebApplicationContext() {
		WebApplicationContext rootContext =
				WebApplicationContextUtils.getWebApplicationContext(getServletContext());
		WebApplicationContext wac = null;

		if (this.webApplicationContext != null) {
			// context实例在构造函数中被注入
			wac = this.webApplicationContext;
			if (wac instanceof ConfigurableWebApplicationContext) {
				ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
				if (!cwac.isActive()) {
					if (cwac.getParent() == null) {
						// 将 Spring 的容器设为 SpringMVC 容器的父容器
						cwac.setParent(rootContext);
					}
					//刷新上下文环境
					configureAndRefreshWebApplicationContext(cwac);
				}
			}
		}
		if (wac == null) {
			// 如果 WebApplicationContext 为空,则进行查找,能找到说明上下文已经在别处初始化。
			wac = findWebApplicationContext();
		}
		if (wac == null) {
			// 如果servlet仍没有context实例,则以 Spring 的容器为父上下文建立一个新的。
			wac = createWebApplicationContext(rootContext);
		}

		if (!this.refreshEventReceived) {
			// Either the context is not a ConfigurableApplicationContext with refresh
			// support or the context injected at construction time had already been
			// refreshed -> trigger initial onRefresh manually here.
			synchronized (this.onRefreshMonitor) {
				// 模版方法,由其子类DispatcherServlet 实现
				onRefresh(wac);
			}
		}

		if (this.publishContext) {
			
			// 发布这个 WebApplicationContext 容器到 ServletContext 中
			String attrName = getServletContextAttributeName();
			getServletContext().setAttribute(attrName, wac);
		}

		return wac;
	}

4. 终于,回到我们的子类DispatcherServlet中执行onRefresh(ApplicationContext context)方法中刷新上下文

IT知识分享网@Override
protected void onRefresh(ApplicationContext context) {
   initStrategies(context);
}

/**
 * Initialize the strategy objects that this servlet uses.
 * <p>May be overridden in subclasses in order to initialize further strategy objects.
 */
protected void initStrategies(ApplicationContext context) {
   //初始化上传文件解析器(或者是多部分请求解析器)
   initMultipartResolver(context);

   //初始化本地化解析器
   initLocaleResolver(context);

   //初始化主题解析器
   initThemeResolver(context);

   //初始化处理器映射器HandlerMapping  --重点
   initHandlerMappings(context);

   //初始化处理器适配器
   initHandlerAdapters(context);

   //初始化处理器异常解析器
   initHandlerExceptionResolvers(context);

   //初始化请求到视图名翻译器
   initRequestToViewNameTranslator(context);

   //初始化视图解析器
   initViewResolvers(context);

   //初始化重定向数据管理器
   initFlashMapManager(context);
}

下面对处理器映射器HandlerMapping类初始化进行一个说明:

initHandlerMappings() 方法从 SpringMVC 的容器及 Spring 的容器中查找所有的 HandlerMapping 实例,并把它们放入到 handlerMappings 这个 list集合 中。这个方法并不是对 HandlerMapping 实例的创建,HandlerMapping 实例是在上面 WebApplicationContext 容器初始化,即 SpringMVC 容器初始化的时候创建的。

private void initHandlerMappings(ApplicationContext context) {
		this.handlerMappings = null;

		if (this.detectAllHandlerMappings) {
			//从SpringMVC的IOC容器及Spring的IOC容器中查找HandlerMapping实例
			Map<String, HandlerMapping> matchingBeans =
					BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
      if (!matchingBeans.isEmpty()) {
				this.handlerMappings = new ArrayList<>(matchingBeans.values());
				// 按照一定排序规则放入list中
				AnnotationAwareOrderComparator.sort(this.handlerMappings);
			}
		}
		else {
			try {
				HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
				this.handlerMappings = Collections.singletonList(hm);
			}
			catch (NoSuchBeanDefinitionException ex) {
				// Ignore, we'll add a default HandlerMapping later.
			}
		}
  // 如果没有,则加载默认的
  if (this.handlerMappings == null) {
			this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
			if (logger.isTraceEnabled()) {
				logger.trace("No HandlerMappings declared for servlet '" + getServletName() +
						"': using default strategies from DispatcherServlet.properties");
			}
		}
}

到此,DispatcherServlet就初始化完成了。

SpringMVC—DispatcherServlet解析[通俗易懂]

初始化完成

然后,调用service方法开始处理http请求。

SpringMVC—DispatcherServlet解析[通俗易懂]

下面,主要讲DispatcherServelt如何处理请求,并封装结果响应。


DispatcherServelt处理请求

在分析处理请求之前,先看一下总体流程:

SpringMVC—DispatcherServlet解析[通俗易懂]

DispatcherServlet处理请求流程

下面是更加详细的版本

SpringMVC—DispatcherServlet解析[通俗易懂]

图片来源:https://blog.csdn.net/win7system/article/details/90674757

1. HttpServlet类处理service

同样的分析,由于service(ServletResquest, ServletResponse)方法其父类HttpServlet已经重写好了,子类没有重写,则调用父类的service()方法。

@Override
public void service(ServletRequest req, ServletResponse res)
    throws ServletException, IOException
{
    HttpServletRequest  request;
    HttpServletResponse response;
    
    if (!(req instanceof HttpServletRequest &&
            res instanceof HttpServletResponse)) {
        throw new ServletException("non-HTTP request or response");
    }

    request = (HttpServletRequest) req;
    response = (HttpServletResponse) res;
  // 调用子类FrameworkServlet判断请求是否是Patch请求或者null, 如果是的话交给子类处理
  // 如果不是返回自己处理
    service(request, response);
}

2. FrameworkServlet类处理service

@Override
	protected void service(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {

		HttpMethod httpMethod = HttpMethod.resolve(request.getMethod());
		if (httpMethod == HttpMethod.PATCH || httpMethod == null) {
			processRequest(request, response);
		}
		else {
      // 返回给HttpServlet类处理
			super.service(request, response);
		}
	}

3 HttpServlet类继续处理service方法

protected void service(HttpServletRequest req, HttpServletResponse resp)
    throws ServletException, IOException {

     // 获取请求方法名称
    String method = req.getMethod();
    // 判断请求方法类型,进入到对应的处理分支
    // Get请求
    if (method.equals(METHOD_GET)) {
        long lastModified = getLastModified(req);
        if (lastModified == -1) {
            // servlet doesn't support if-modified-since, no reason
            // to go through further expensive logic
            doGet(req, resp);
        } else {
            long ifModifiedSince;
            try {
                ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
            } catch (IllegalArgumentException iae) {
                // Invalid date header - proceed as if none was set
                ifModifiedSince = -1;
            }
            if (ifModifiedSince < (lastModified / 1000 * 1000)) {
                // If the servlet mod time is later, call doGet()
                // Round down to the nearest second for a proper compare
                // A ifModifiedSince of -1 will always be less
                maybeSetLastModified(resp, lastModified);
                doGet(req, resp);
            } else {
                resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
            }
        }

      // Head请求
    } else if (method.equals(METHOD_HEAD)) {
        long lastModified = getLastModified(req);
        maybeSetLastModified(resp, lastModified);
        doHead(req, resp);

      // Post
    } else if (method.equals(METHOD_POST)) {
        doPost(req, resp);

      // Put
    } else if (method.equals(METHOD_PUT)) {
        doPut(req, resp);

      // Delete
    } else if (method.equals(METHOD_DELETE)) {
        doDelete(req, resp);

      // Options
    } else if (method.equals(METHOD_OPTIONS)) {
        doOptions(req,resp);

      // Trace
    } else if (method.equals(METHOD_TRACE)) {
        doTrace(req,resp);

    } else {
        // 什么请求都不匹配,返回error
        String errMsg = lStrings.getString("http.method_not_implemented");
        Object[] errArgs = new Object[1];
        errArgs[0] = method;
        errMsg = MessageFormat.format(errMsg, errArgs);

        resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
    }
}

4. 以Get请求为例,下面调用doGet请求,由于HttpServlet的子类FrameworkServlet类重写了HttpServlet的doGet方法,因此,将调用FrameworkServlet类的doGet请求。

@Override
protected final void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {

   processRequest(request, response);
}

protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {

		long startTime = System.currentTimeMillis();
		Throwable failureCause = null;

		LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
		LocaleContext localeContext = buildLocaleContext(request);

		RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
		ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);

		WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
		asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());

		initContextHolders(request, localeContext, requestAttributes);

		try {
      // 终于,在这一步,子类DispatcherServlet重写了doService方法
			doService(request, response);
		}
		catch (ServletException | IOException ex) {
			failureCause = ex;
			throw ex;
		}
		catch (Throwable ex) {
			failureCause = ex;
			throw new NestedServletException("Request processing failed", ex);
		}

		finally {
			resetContextHolders(request, previousLocaleContext, previousAttributes);
			if (requestAttributes != null) {
				requestAttributes.requestCompleted();
			}
			logResult(request, response, failureCause, asyncManager);
			publishRequestHandledEvent(request, response, startTime, failureCause);
		}
	}

5. 千呼万唤,终于进入到DispatcherServlet类的doService()方法中

在doService方法中,主要是对请求做一些赋值,然后重中之重调用doDispatch方法处理请求。

@Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
   logRequest(request);

   // Keep a snapshot of the request attributes in case of an include,
   // to be able to restore the original attributes after the include.
   Map<String, Object> attributesSnapshot = null;
   if (WebUtils.isIncludeRequest(request)) {
      attributesSnapshot = new HashMap<>();
      Enumeration<?> attrNames = request.getAttributeNames();
      while (attrNames.hasMoreElements()) {
         String attrName = (String) attrNames.nextElement();
         if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) {
            attributesSnapshot.put(attrName, request.getAttribute(attrName));
         }
      }
   }

   // Make framework objects available to handlers and view objects.
   request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
   request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
   request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
   request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());

   if (this.flashMapManager != null) {
      FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
      if (inputFlashMap != null) {
         request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
      }
      request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
      request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);
   }

   RequestPath previousRequestPath = null;
   if (this.parseRequestPath) {
      previousRequestPath = (RequestPath) request.getAttribute(ServletRequestPathUtils.PATH_ATTRIBUTE);
      ServletRequestPathUtils.parseAndCache(request);
   }

   try {
     // 终于要处理request啦
      doDispatch(request, response);
   }
   finally {
      if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
         // Restore the original attribute snapshot, in case of an include.
         if (attributesSnapshot != null) {
            restoreAttributesAfterInclude(request, attributesSnapshot);
         }
      }
      ServletRequestPathUtils.setParsedRequestPath(previousRequestPath, request);
   }
}

6. DispatcherServlet的doDispatch处理请求和返回响应

doDispatch处理的大致流程:

  • 通过HandlerMapping获取请求对应的Handler
  • 再通过Handler找到对应的HandlerAdapter
  • HandlerAdapterd调用handler方法得到ModelAndView(连接“业务逻辑层”与“视图展示层”的桥梁
  • 通过ModelAndView 获得 View,再通过它的 Model 对 View 进行渲染
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
   HttpServletRequest processedRequest = request;
   HandlerExecutionChain mappedHandler = null;
   boolean multipartRequestParsed = false;

   WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

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

      try {
         //1. 判断是否是文件上传请求
         processedRequest = checkMultipart(request);
         multipartRequestParsed = (processedRequest != request);

         // 2. 遍历所有的 HandlerMapping 找到与请求对应的 Handler,并将其与一堆拦截器封装到 HandlerExecutionChain 对象中。
         // 返回的HandlerExecutionChain请求处理器链对象封装了handler和interceptors.
         mappedHandler = getHandler(processedRequest);

         // 如果没有对应的handler,返回404
         if (mappedHandler == null) {
            noHandlerFound(processedRequest, response);
            return;
         }

         // 3. 获取处理请求的处理器适配器HandlerAdapter
         HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
         
         // 处理 last-modified 请求头
         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;
            }
         }

         // 请求方法前置拦截,如果返回true 表示会执行到目标方法(请求方法) 如果返回false的情况下,直接return。
         if (!mappedHandler.applyPreHandle(processedRequest, response)) {
            return;
         }
         
         // 5. 这一步就是调用对应的controller控制器,并返回结果视图对象
         mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
         
         // 如果是异步模式,则返回
         if (asyncManager.isConcurrentHandlingStarted()) {
            return;
         }

         // 6. 如果mv不为null,则给其ViewName设置初始默认值
         applyDefaultViewName(processedRequest, mv);

         // 7. 后置拦截器的postHandle方法的处理
         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);
      }
      // 8. 处理返回结果;(异常处理、页面渲染、拦截器的afterCompletion触发等)
      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()) {
         //判断是否执行异步请求
         if (mappedHandler != null) {
            // 如果是的话,就替代拦截器的postHandle 和 afterCompletion方法执行
            mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
         }
      }
      else {
         // 删除上传请求的资源
         if (multipartRequestParsed) {
            cleanupMultipart(processedRequest);
         }
      }
   }
}

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://yundeesoft.com/7033.html

(0)
上一篇 2023-01-03 09:53
下一篇 2023-01-03 09:53

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注微信