Spring MVC 源码之申请的执行流程

DispatcherServlet

SpringMVC外围就是DispatcherServlet,所有得申请都会转发到DispatcherServlet,而后再通过DispatcherServlet执行具体得管制层(Handler)返回ModelAndView给客户端视图展现。

DispatcherServlet其实就是一个Servlet类,无非就是包装一层,通过url可能映射找到咱们得SpringMvc中定义得申请办法。

  1. 类的集成关系

    DispatcherServlet继承FrameworkServlet继承HttpServlet

    面向基本上思维 重写 先走父类 ,在走子类。

    得出答案:先看HttpServlet再找到咱们最初的子类

    当咱们第一次申请一个地址的时候,如果可能拜访,他会返回一个200代表拜访胜利,此时应答头信息中会有一个 Last-Modified 代表服务器这个文件的最初批改工夫。

    当咱们再次申请的时候,如果申请过了,就会在申请头,有一个If-Modified-Since的值,这时候传到服务器,服务器就会拿这个值和上次批改的工夫比照,如果小于上次批改的工夫,阐明服务器上的文件被批改过,就再次从服务器进行下载,返回200

    如果没有批改就像上图一样,返回一个304,代表客户端曾经执行了GET,但文件未变动。

既然是Servlet类,那么他有一个最终的办法,就是service()办法,他是Servlet最外围的办法。

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 = req.getDateHeader(HEADER_IFMODSINCE);                if (ifModifiedSince < lastModified) {                    // 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);        }    }

maybeSetLastModified(resp, lastModified);

    /*     * Sets the Last-Modified entity header field, if it has not     * already been set and if the value is meaningful.  Called before     * doGet, to ensure that headers are set before response data is     * written.  A subclass might have set this header already, so we     * check.     */    private void maybeSetLastModified(HttpServletResponse resp,                                      long lastModified) {        if (resp.containsHeader(HEADER_LASTMOD))            return;        if (lastModified >= 0)            resp.setDateHeader(HEADER_LASTMOD, lastModified);    }

因而,咱们在HttpServletBean类中找service办法,发现没有,咱们持续往上一层FrameworkServlet类中找,发现找到了,因而spring实现该办法在这个类去实现的。

    /**     * Override the parent class implementation in order to intercept PATCH requests.     */    @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);        }  }

这里职责次要是先拿到一个申请,而后又做了一个判断申请形式。发现不是PATCH形式就去调用父类(HttpServlet)中service()办法。他去调用用父类中的service办法其实就是去调用该类中doPost(),doGet()办法,拿到不同的申请形式而后解决不同的业务。比方以FrameworkServlet的get形式为例:

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

FrameWorkServlet#processRequest

这个办法外面能够间接看到this.doService(request, response);办法。在debug期间,进去该办法,发现这个办法间接跳到DispatcherServlet 类中,由上可知,这个办法就像始终被子类重写。

    /**     * Exposes the DispatcherServlet-specific request attributes and delegates to {@link #doDispatch}     * for the actual dispatching.     */    @Override    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);                }            }        }  }

进入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.                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);                }            }        }    }

首先次要是创立一个视图对象 ModelAndView mv = null;而后查看以后申请是否是二进制的申请processedRequest = this.checkMultipart(request);而后就是只有代码

mappedHandler = this.getHandler(processedRequest);

就是依据以后的申请去拿一个Handler.(在这个源码中springMVC都是应用的Handler,那么他到时是什么?这个Handler其实就是咱们的控制器,包含咱们写Controller)。进入这个办法源码如下:

    /**     * Return the HandlerExecutionChain for this request.     * <p>Tries all handler mappings in order.     * @param request current HTTP request     * @return the HandlerExecutionChain, or {@code null} if no handler could be found     */    @Nullable    protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {        if (this.handlerMappings != null) {            for (HandlerMapping mapping : this.handlerMappings) {                HandlerExecutionChain handler = mapping.getHandler(request);                if (handler != null) {                    return handler;                }            }        }        return null;    }

由流程图可知,发送清求到控制器,控制器第二个节点就是发送第二个申请就是去拿Handler,因而可知这里才是最外围代码。由图可知他取Handler最终要去找HandlerMapping,而后他再去拿一个Handler。那么为什么要去找HandlerMapping去要一个Handler呢?

首先咱们在配置控制器的时候有两种形式1.xml形式,2.注解的形式。因而spring源码他给咱们不止一种控制器 。因为两种形式控制器 。因而spring并不知道咱们应用的是哪一种控制器。因为两种控制器,spring去底层去找的管制的实现形式是不一样的。因而这就是为什么第二步他要去找Handler(控制器)的了。然而Handler怎么找的到呢?就是通过HandlerMapping这样一个处理器映射器。

Handler分装了咱们创立的Controller和一个拦截器。

因而到这里咱们就拿到了对应的也是最合适的Handler,而后返回中央处理器。

第二个办法:

HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());

获取控制器的适配器。也就是咱们之前拿到了控制器,接下来要去执行控制器,也就是拿到控制器适配器中执行控制器。这里为什么要获取适配器呢?因为跟控制器映射器(也就是配置形式)一样。

接下来判断你有没有须要执行的拦截器。:

if (!mappedHandler.applyPreHandle(processedRequest, response)) {return;}
    /**     * Apply preHandle methods of registered interceptors.     * @return {@code true} if the execution chain should proceed with the     * next interceptor or the handler itself. Else, DispatcherServlet assumes     * that this interceptor has already dealt with the response itself.     */    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;                }                this.interceptorIndex = i;            }        }        return true;    }

适配器去执行Handler

mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

如果你有ModelAndView,就返回一个ModelAndView.而后返回给试图对象,而后把视图对象交给视图解析器,去渲染,最初响应给用户。

因而总结,spring提供了两种HandlerMapping以及三种HandlerAdapter.他们运行匹配的关系如图:

那么运行时他怎么能找到这些呢?spring是怎么配置提供的呢?

其实他们在spring配置文件就曾经配置好了,当springMVC初始化时就加载实例化,获取这些对象。他们是被配置在spring的SpringwebMVC架包的servlet架包中的DispatcherServlet.properties配置文件中

DispatcherServlet源码流程剖析

1.执行doDispatch

2.调用getHandler办法获取申请指标的办法 也就是 申请url映射门路对应的管制层具体的办法

handlerMappings的作用查找控制器地位,比方xml和注解形式。

3.调用getHandlerAdapter获取管制层适配器 RequestMappingHandlerAdapter

4.执行拦截器前置办法 preHandle() 如果返回为true的话

5.执行理论申请指标办法 返回modeAndView对象

6.执行拦截器PostHandle()办法

7.设置渲染视图层内容

8.执行拦截器afterCompletion办法

HandlerMappings

DispatcherServlet#initHandlerMappings

/** * Initialize the HandlerMappings used by this class. * <p>If no HandlerMapping beans are defined in the BeanFactory for this namespace, * we default to BeanNameUrlHandlerMapping. */private void initHandlerMappings(ApplicationContext context) {   this.handlerMappings = null;   if (this.detectAllHandlerMappings) {      // Find all HandlerMappings in the ApplicationContext, including ancestor contexts.      Map<String, HandlerMapping> matchingBeans =            BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);      if (!matchingBeans.isEmpty()) {         this.handlerMappings = new ArrayList<>(matchingBeans.values());         // We keep HandlerMappings in sorted order.         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.      }   }   // Ensure we have at least one HandlerMapping, by registering   // a default HandlerMapping if no other mappings are found.   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");      }   }}

servlet初始化会调用 init 办法,换句话说就是springMVC进行初始化的时候首先会去执行HttpServletBean的init办法, 上面看看HttpServletBean的源码:

    /**     * Map config parameters onto bean properties of this servlet, and     * invoke subclass initialization.     * @throws ServletException if bean properties are invalid (or required     * properties are missing), or if subclass initialization fails.     */    @Override    public final void init() throws ServletException {        // Set bean properties from init parameters.        PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);        if (!pvs.isEmpty()) {            try {                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();    }

获取你在web.xml中配置在<init-param>中的属性(例如: namespace, contextConfigLocation)。 其中有一点值得注意,那就是 initServletBean() 这个办法是由其子类 FrameworkServlet 实现,因而, 接下来 FramworkServlet 会执行 initServletBean 这个办法,上面就持续看看 initServletBean 办法源码:
FrameWorkservlet#initServletBean

    @Override    protected final void initServletBean() throws ServletException {        getServletContext().log("Initializing Spring " + getClass().getSimpleName() + " '" + getServletName() + "'");        if (logger.isInfoEnabled()) {            logger.info("Initializing Servlet '" + getServletName() + "'");        }        long startTime = System.currentTimeMillis();        try {            this.webApplicationContext = initWebApplicationContext();            initFrameworkServlet();        }        catch (ServletException | RuntimeException ex) {            logger.error("Context initialization failed", ex);            throw ex;        }        if (logger.isDebugEnabled()) {            String value = this.enableLoggingRequestDetails ?                    "shown which may lead to unsafe logging of potentially sensitive data" :                    "masked to prevent unsafe logging of potentially sensitive data";            logger.debug("enableLoggingRequestDetails='" + this.enableLoggingRequestDetails +                    "': request parameters and headers will be " + value);        }        if (logger.isInfoEnabled()) {            logger.info("Completed initialization in " + (System.currentTimeMillis() - startTime) + " ms");        }    }

initServletBean 办法中就调用了一个 initFrameworkServlet 办法和 initWebApplicationContext 办法,其中initFrameworkServlet办法是由子类实现,这个不多说,间接看 initWebApplicationContext 办法源码:

protected WebApplicationContext initWebApplicationContext() {         //此处的 rootContext 在你配置了ContextLoaderListener的时候注入的        //通过剖析ContextLoaderListenr的源码,能够看到        //ContextLoaderListener通过ContextLoader依据ApplicationContext.xml的配置会创立一个xmlWebApplicationContext        //如果没有配置ContextLoaderListener,本处将为null,但不影响springMVC,为何?通过接下来的剖析,就能看到起因        WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(this.getServletContext());        WebApplicationContext wac = null;              //当webApplicationContext曾经存在,那么就间接应用,应用之前会先设置rootContext,为其跟。       //配置实现之后refresh一次,refresh会波及到IOC的内容,本处不做探讨。         if (this.webApplicationContext != null) {            wac = this.webApplicationContext;            if (wac instanceof ConfigurableWebApplicationContext) {                ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext)wac;                if (!cwac.isActive()) {                    if (cwac.getParent() == null) {                        cwac.setParent(rootContext);                    }                     this.configureAndRefreshWebApplicationContext(cwac);                }            }        }        //如果不存在webApplicationContext,那么先去ServletContext中查找        if (wac == null) {            wac = this.findWebApplicationContext();        }        //如果上述没有查到,那么就创立webApplicationContext        if (wac == null) {            wac = this.createWebApplicationContext(rootContext);        }         if (!this.refreshEventReceived) {             //此办法由DispatcherServlet调用            this.onRefresh(wac);        }        //将webApplicationContext保留在ServletContext        if (this.publishContext) {            //将上下文公布为servlet上下文属性。            String attrName = this.getServletContextAttributeName();            this.getServletContext().setAttribute(attrName, wac);            if (this.logger.isDebugEnabled()) {                this.logger.debug("Published WebApplicationContext of servlet '" + this.getServletName() + "' as ServletContext attribute with name [" + attrName + "]");            }        }         return wac;    }
protected WebApplicationContext createWebApplicationContext(@Nullable ApplicationContext parent) {               //此处的contextClass 可在web.xml 中的《init-param》中指定       //如果没有配置,那么默认的是XmlWebApplicationContext.class        Class<?> contextClass = this.getContextClass();        if (this.logger.isDebugEnabled()) {            this.logger.debug("Servlet with name '" + this.getServletName() + "' will try to create custom WebApplicationContext context of class '" + contextClass.getName() + "', using parent context [" + parent + "]");        }         if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {            throw new ApplicationContextException("Fatal initialization error in servlet with name '" + this.getServletName() + "': custom WebApplicationContext class [" + contextClass.getName() + "] is not of type ConfigurableWebApplicationContext");        } else {             //此处利用反射创立            ConfigurableWebApplicationContext wac = (ConfigurableWebApplicationContext)BeanUtils.instantiateClass(contextClass);            wac.setEnvironment(this.getEnvironment());            wac.setParent(parent);            String configLocation = this.getContextConfigLocation();            if (configLocation != null) {                wac.setConfigLocation(configLocation);            }            //refresh一次,这里不多说            this.configureAndRefreshWebApplicationContext(wac);            return wac;        }

SpringMVC管制层容器初始化

  1. HttpServletBean init ()办法
  2. FrameworkServlet initServletBean办法→ initWebApplicationContext();
  3. DispatcherServlet onRefresh办法→ initStrategies()办法