Spring MVC 源码分析之 DispatcherServlet

目录

一、概述

二、类图

三、初始化过程

四、请求过程

五、总结


一、概述

MVC大家比较熟悉

  • M即model,是业务处理层,与我们开发中的(service、dao、model)等对应起来;
  • V即view,是视图层,以前jsp、freemarker、velocity等,现在都是前后端分离了。使用@ResponseBody注解把Controller方法返回的对象通过转换器转换成指定的格式(如json/xml/protobuf)后,再写入到Response对象的body区,不再走视图解析器,把渲染到工作交给前端去做。
  • C,即controller,控制器,可以分为前端控制器(负责请求的分发,即DispatcherServlet)、映射处理器(uri与处理方法的映射,即HandlerMapping)、业务控制器(即我们的controller层)、视图解析器(即ViewResolver)。

二、类图

Spring MVC 源码分析之 DispatcherServlet_第1张图片

如上图类的继承关系可知,DispatcherServlet就是一个Servlet.

三、初始化过程

了解Servlet的都知道在Servlet中主要的方法有:

  • init  初始化方法
  • service 用于处理请求的方法
  • destroy servlet的销毁方法

3.1、HttpServletBean类初始化方法 init

public final void init() throws ServletException {

		// Set bean properties from init parameters.
		PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
		if (!pvs.isEmpty()) {
			try {
                //将Servlet中配置的参数封装到pvs变量中,requiredProperties为必须参数,如果没
                //配置将报异常
				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;
			}
		}

		// Let subclasses do whatever initialization they like.
        // 模板方法,子类初始化入口方法,在子类中可以做一些具体的实现方法
		initServletBean();
	}

  3.2、在 FrameworkServlet 中实现了 initServletBean 方法

@Override
	protected final void initServletBean() throws ServletException {
		// 略 .....
		try {
           
            //主要方法,主要实现了 初始化 WebApplicationContext 
			this.webApplicationContext = initWebApplicationContext();
			initFrameworkServlet();
		}
		catch (ServletException | RuntimeException ex) {
			logger.error("Context initialization failed", ex);
			throw ex;
		}
         // 略 .....
		}
	}

3.3、initWebApplicationContext 方法

protected WebApplicationContext initWebApplicationContext() {
		
      //  略...

		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) {
				onRefresh(wac);  //重点方法
			}
		}

		if (this.publishContext) {
			// Publish the context as a servlet context attribute.
			String attrName = getServletContextAttributeName();
			getServletContext().setAttribute(attrName, wac);
		}

		return wac;
	}

 

initWebApplicationContext方法做了三件事:

  1.    获取spring的根容器rootContext。
  2.    设置webApplicationContext并根据情况调用onRefresh方法。
  3.    将webApplicationContext设置到ServletContext中。

3.4、DispatcherServlet中的 onRefresh 方法

@Override
	protected void onRefresh(ApplicationContext context) {
		initStrategies(context);
	}

	/**
	 * Initialize the strategy objects that this servlet uses.
	 * 

May be overridden in subclasses in order to initialize further strategy objects. */ protected void initStrategies(ApplicationContext context) { //初始化多媒体解析器,这个不分析 initMultipartResolver(context);    //初始化国际化解析器,这个不进行分析 initLocaleResolver(context);    //初始化主题解析器,基本不用,不进行分析 initThemeResolver(context);    //初始化映射器 initHandlerMappings(context);    //初始化适配器 initHandlerAdapters(context);    //初始化异常解析器 initHandlerExceptionResolvers(context);    //初始化视图名转换器,不进行分析 initRequestToViewNameTranslator(context);    //初始化视图解析器,前端分离后,用得少了,也分析下 initViewResolvers(context);    //初始化FlashMapManager,只知道是重定向时用来保存数据用的,没有使用过,这里不进行分析 initFlashMapManager(context); }

下面贴的代码的几个初始化代码的逻辑其实是一样的,分为三步,以initHandlerMappings进行说明

  1.   判断是否寻找所有容器中实现HandlerMapping接口的,是则寻找所有容器中实现了HandlerMapping接口的对象,设置到handlerMappings对象中并排序(平常开发中走的都是这个分支)
  2.   否的话,只找寻到前容器实现了HandlerMapping接口的对象
  3.   如果上面查找后为空的话,则加载配置文件中的类并实例化,再设置到handlerMappings中。注意的是:这个配置文件位置是:org/springframework/web/servlet/DispatcherServlet.properties

至此,DispatcherServlet的初始化工作已经完成。

四、请求过程

上文介绍过 Servlet中处理请求的为 service 方法。下面先看service方法的处理逻辑

4.1、HttpServlet中的 service方法

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

        String method = req.getMethod();

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

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

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

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

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

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

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

        } else {
            //
            // Note that this means NO servlet supports whatever
            // method was requested, anywhere on this server.
            //

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

从源码中可得知 HttpServlet中 service 方法根据请求方法,把请求具体交给了 doGet、doPost、doPut等方法来处理

4.2、FrameworkServlet 中的 service 方法

在FrameworkServlet中重写了 service 方法同时也重写了 doGet、doPost,doPut等方法,源码如下所示

@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 {
			super.service(request, response);
		}
	}


	@Override
	protected final void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
        
        // 主要通过此方法实现请求的分发
		processRequest(request, response);
	}

有源码可知,在doGet 方法中有调用了 processRequest方法,其他doXX中同样调用了此方法,接下来看看 此方法。

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

		long startTime = System.currentTimeMillis();
		Throwable failureCause = null;
         // 获取LocaleContextHolder 中保存的LocalContext
		LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
 
        //获取当前请求的LocaleContext
		LocaleContext localeContext = buildLocaleContext(request);
        
        //获取RequestContextHolder中保存的RequestAttributes
		RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
        //获取当前请求的ServletRequestAttributes
		ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);

		// 略 .....
         
        //将当前请求的LocaleContext和ServletRequestAttributes保存到
        //LocaleConextHolder和RequestContextHolder中
		initContextHolders(request, localeContext, requestAttributes);

		try {
            //模板方法,在子类中实现
			doService(request, response);
		}catch (ServletException | IOException ex) {
			//略 ....
		}catch (Throwable ex) {
			//略 ....
		}finally {
             //恢复之前的LocaleContext和ServletRequestAttributes到
             //LocaleConextHolder和RequestContextHolder中
			resetContextHolders(request, previousLocaleContext, previousAttributes);
			if (requestAttributes != null) {
				requestAttributes.requestCompleted();
			}
			
            //发布ServletRequestHandledEvent消息
			publishRequestHandledEvent(request, response, startTime, failureCause);
		}
	}

  说明: 由于在请求时把LocaleContext和ServletRequestAttributes保存到了LocaleContextHolder和RequestContextHolder中,这两个类都是在ThreadLocal中所有在后续的方法中可以根据通过这两个类获取LocalContext和ServletRequestAttributes,从而获取HttpServletRequest、HttpServletResponse和HttpSession。

 4.3、DispatcherServlet中实现了 doService方法,

protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
		
         //省略 部分代码 .....

		try {

            //真正处理 请求转发的方法
			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);
				}
			}
		}
	}

有源码可知 doService方法中主要是调用了 doDispatch方法,接下来看看此方法的实现

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 {
                //检测是否为上传请求
				processedRequest = checkMultipart(request);
				multipartRequestParsed = (processedRequest != request);

				// Determine handler for the current request.
                //根据 request 查找对应的 Handler 
				mappedHandler = getHandler(processedRequest);
				if (mappedHandler == null) {
					noHandlerFound(processedRequest, response);
					return;
				}

				// Determine handler adapter for the current request.
                //根据 Handler查找执行此Handler的 handler适配器
				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.
                //实际调用 Handler
				mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

				if (asyncManager.isConcurrentHandlingStarted()) {
					return;
				}
         
				applyDefaultViewName(processedRequest, mv);
                //执行拦截器中的 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);
			}
            //处理返回结果。包括处理异常、渲染页面、发出完成通知调用拦截器的 afterComletion方法
			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);
				}
			}
		}
	}

五、总结

 本篇文章主要是分析了DispatcherServlet 的初始化和请求的响应过程,接下来将会分析 Handler的查找过程。

 

你可能感兴趣的:(Spring,FrameWork,SpringMVC,spring,mvc,spring,boot,SpingMVC)