Spring MVC体系结构与执行流程

 2022-08-13
原文地址:https://cloud.tencent.com/developer/article/1412605

Spring MVC介绍(一)之 Spring MVC体系结构与执行流程

Spring MVC是Spring的一个模块,包含Web MVC、Framework,Web Views,JSP等等。 其中MVC分别对应:Model,View,Controller,本质上SpirngMVC还是使用Servlet进行处理,并在其基础上进行了封装,简化了开发流程,提高易用性,并使得逻辑结构变得非常清晰。

202208132149320551.png

Spring MVC执行流程.png

Spring MVC对比Servlet简化开发流程的方面有:

  • 基于注解的url映射
  • http表单参数转换
  • 全局统一异常处理
  • 拦截器的实现 等等。
Spring MVC 体系结构

HandlerMapping : url与控制器的映射

    package org.springframework.web.servlet;
    
    import javax.servlet.http.HttpServletRequest;
    
    public interface HandlerMapping {
        String PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE = HandlerMapping.class.getName() + ".pathWithinHandlerMapping";
        String BEST_MATCHING_PATTERN_ATTRIBUTE = HandlerMapping.class.getName() + ".bestMatchingPattern";
        String INTROSPECT_TYPE_LEVEL_MAPPING = HandlerMapping.class.getName() + ".introspectTypeLevelMapping";
        String URI_TEMPLATE_VARIABLES_ATTRIBUTE = HandlerMapping.class.getName() + ".uriTemplateVariables";
        String MATRIX_VARIABLES_ATTRIBUTE = HandlerMapping.class.getName() + ".matrixVariables";
        String PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE = HandlerMapping.class.getName() + ".producibleMediaTypes";
    
        HandlerExecutionChain getHandler(HttpServletRequest var1) throws Exception;
    }

HandlerAdapter : 控制器与执行适配器

    package org.springframework.web.servlet;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    public interface HandlerAdapter {
        boolean supports(Object var1);
    
        ModelAndView handle(HttpServletRequest var1, HttpServletResponse var2, Object var3) throws Exception;
    
        long getLastModified(HttpServletRequest var1, Object var2);
    }

ViewResolver : 视图仓库

    package org.springframework.web.servlet;
    
    import java.util.Locale;
    
    public interface ViewResolver {
        View resolveViewName(String var1, Locale var2) throws Exception;
    }

View : 视图

    package org.springframework.web.servlet;
    
    import java.util.Map;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    public interface View {
        String RESPONSE_STATUS_ATTRIBUTE = View.class.getName() + ".responseStatus";
        String PATH_VARIABLES = View.class.getName() + ".pathVariables";
        String SELECTED_CONTENT_TYPE = View.class.getName() + ".selectedContentType";
    
        String getContentType();
    
        void render(Map<String, ?> var1, HttpServletRequest var2, HttpServletResponse var3) throws Exception;
    }

HandlerExceptionResolver : 异常捕捉器

    package org.springframework.web.servlet;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    public interface HandlerExceptionResolver {
        ModelAndView resolveException(HttpServletRequest var1, HttpServletResponse var2, Object var3, Exception var4);
    }

HandlerInterceptor : 拦截器

    package org.springframework.web.servlet;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    public interface HandlerInterceptor {
        boolean preHandle(HttpServletRequest var1, HttpServletResponse var2, Object var3) throws Exception;
    
        void postHandle(HttpServletRequest var1, HttpServletResponse var2, Object var3, ModelAndView var4) throws Exception;
    
        void afterCompletion(HttpServletRequest var1, HttpServletResponse var2, Object var3, Exception var4) throws Exception;
    }
Spring MVC 执行流程

先看个简单的demo

web.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee"
             xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
             id="WebApp_ID" version="3.0">
    
        <display-name>spring-mvc</display-name>
        <servlet>
            <servlet-name>dispatcherServlet</servlet-name>
            <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
            <init-param>
                <param-name>contextConfigLocation</param-name>
                <param-value>
                    classpath:/spring-mvc.xml
                </param-value>
            </init-param>
        </servlet>
        <servlet-mapping>
            <servlet-name>dispatcherServlet</servlet-name>
            <url-pattern>/</url-pattern>
        </servlet-mapping>
    
        <welcome-file-list>
            <welcome-file>/index.jsp</welcome-file>
        </welcome-file-list>
    </web-app>

spring-mvc.xml

    <?xml version="1.0" encoding="GBK"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:context="http://www.springframework.org/schema/context"
           xmlns:mvc="http://www.springframework.org/schema/mvc"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context.xsd
           http://www.springframework.org/schema/mvc
           http://www.springframework.org/schema/mvc/spring-mvc.xsd">
        
        <bean id="simple" class="com.demo.spring.mvc.control.SimpleControl"></bean>
        
        <bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
            <property name="urlMap">
                <props>
                    <prop key="/hello.do">simple</prop>
                </props>
            </property>
        </bean>
    
        <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"></bean>
    
        <!-- 视图仓库 -->
        <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
            <property name="prefix" value="/WEB-INF/page/"/>
            <property name="suffix" value=".jsp"/>
            <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
        </bean>
    </beans>

userView.jsp

    <%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
    <html>
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>userView</title>
    </head>
    <body>
        ${name}
    </body>
    </html>

SimpleControl.java

    package com.demo.spring.mvc.control;
    
    
    import org.springframework.web.servlet.ModelAndView;
    import org.springframework.web.servlet.mvc.Controller;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    /**
     * com.demo.spring.mvc.control
     *
     * @author Zyy
     * @date 2019/2/20 22:47
     */
    public class SimpleControl implements Controller {
    
        @Override
        public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
            ModelAndView modelAndView = new ModelAndView("userView");
            modelAndView.addObject("name","ayang");
            return modelAndView;
        }
    }

启动后访问localhost:8080/hello.do 就可以显示我们jsp中我们返回的name了。

整个mvc流程如下:

    浏览器访问/hello.do -> 请求dispatcher servlet -> 然后找到SimpleUrlHandlerMapping
    -> 然后根据配置找到/hello.do对应simple ref -> 然后根据simple ref找到
    simple这个control bean -> 然后根据control会找到对应的适配器SimpleControllerHandlerAdapter
    -> 基于适配器执行业务处理 -> 然后返回给dispatcher Servlet -> 然后找到视图仓库InternalResourceViewResolver ->
    然后视图仓库根据viewName找到对应的视图解析JstlView ->然后返回给view

Spring MVC 体系结构UML

202208132149334062.png

Spirng MVC 体系结构UML.png

从上图可以看出,其中最主要的就是DispatcherServlet,看一下相关的源码。

org.springframework.web.servlet.DispatcherServlet 核心的方法:

    protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        HttpServletRequest processedRequest = request;
        HandlerExecutionChain mappedHandler = null;
        boolean multipartRequestParsed = false;
        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
    
        try {
            try {
                ModelAndView mv = null;
                Object dispatchException = null;
    
                try {
                    processedRequest = this.checkMultipart(request);
                    multipartRequestParsed = processedRequest != request;
                    //获取mappedHandler
                    mappedHandler = this.getHandler(processedRequest);
                    if (mappedHandler == null || mappedHandler.getHandler() == null) {
                        this.noHandlerFound(processedRequest, response);
                        return;
                    }
                    //根据handler获取HandlerAdapter
                    HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
                    String method = request.getMethod();
                    boolean isGet = "GET".equals(method);
                    if (isGet || "HEAD".equals(method)) {
                        long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
                        if (this.logger.isDebugEnabled()) {
                            this.logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
                        }
    
                        if ((new ServletWebRequest(request, response)).checkNotModified(lastModified) && isGet) {
                            return;
                        }
                    }
    
                    if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                        return;
                    }
                    
                    //HandlerAdapter找到我们配置的SimpleControllerHandlerAdapter,
                    //然后由此找到我们自己的SimpleControl
                    mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
                    if (asyncManager.isConcurrentHandlingStarted()) {
                        return;
                    }
    
                    this.applyDefaultViewName(processedRequest, mv);
                    mappedHandler.applyPostHandle(processedRequest, response, mv);
                } catch (Exception var20) {
                    dispatchException = var20;
                } catch (Throwable var21) {
                    dispatchException = new NestedServletException("Handler dispatch failed", var21);
                }
    
                this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);
            } catch (Exception var22) {
                this.triggerAfterCompletion(processedRequest, response, mappedHandler, var22);
            } catch (Throwable var23) {
                this.triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", var23));
            }
    
        } finally {
            if (asyncManager.isConcurrentHandlingStarted()) {
                if (mappedHandler != null) {
                    mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
                }
            } else if (multipartRequestParsed) {
                this.cleanupMultipart(processedRequest);
            }
    
        }
    }

其中首先根据request进行 getHandler(HttpServletRequest request) 方法,返回一个 HandlerExecutionChain ,方法是遍历 handlerMappings ,然后从 handlerMappings 中获取 HandlerExecutionChain ,相关代码如下:

    protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
            Iterator var2 = this.handlerMappings.iterator();
    
            HandlerExecutionChain handler;
            do {
                if (!var2.hasNext()) {
                    return null;
                }
    
                HandlerMapping hm = (HandlerMapping)var2.next();
                if (this.logger.isTraceEnabled()) {
                    this.logger.trace("Testing handler map [" + hm + "] in DispatcherServlet with name '" + this.getServletName() + "'");
                }
    
                handler = hm.getHandler(request);
            } while(handler == null);
    
            return handler;
        }

上面方法中 HandlerMapping.getHandler(request) 源码如下:

    org.springframework.web.servlet.handler.AbstractHandlerMapping#getHandler
    
    public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
        Object handler = this.getHandlerInternal(request);
        if (handler == null) {
            handler = this.getDefaultHandler();
        }
    
        if (handler == null) {
            return null;
        } else {
            if (handler instanceof String) {
                String handlerName = (String)handler;
                handler = this.getApplicationContext().getBean(handlerName);
            }
    
            HandlerExecutionChain executionChain = this.getHandlerExecutionChain(handler, request);
            if (CorsUtils.isCorsRequest(request)) {
                CorsConfiguration globalConfig = this.corsConfigSource.getCorsConfiguration(request);
                CorsConfiguration handlerConfig = this.getCorsConfiguration(handler, request);
                CorsConfiguration config = globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig;
                executionChain = this.getCorsHandlerExecutionChain(request, executionChain, config);
            }
    
            return executionChain;
        }
    }

上面方法中 getHandlerExecutionChain(handler, request) 源码如下:

    protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
        HandlerExecutionChain chain = handler instanceof HandlerExecutionChain ? (HandlerExecutionChain)handler : new HandlerExecutionChain(handler);
        String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
        Iterator var5 = this.adaptedInterceptors.iterator();
    
        while(var5.hasNext()) {
            HandlerInterceptor interceptor = (HandlerInterceptor)var5.next();
            if (interceptor instanceof MappedInterceptor) {
                MappedInterceptor mappedInterceptor = (MappedInterceptor)interceptor;
                if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
                    chain.addInterceptor(mappedInterceptor.getInterceptor());
                }
            } else {
                chain.addInterceptor(interceptor);
            }
        }
    
        return chain;
    }

继续回到doDispatch,根据获取到的handler进行继续获取适配器,方法: getHandlerAdapter 相关源码如下:

    org.springframework.web.servlet.DispatcherServlet#getHandlerAdapter
    
    protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
        Iterator var2 = this.handlerAdapters.iterator();
    
        HandlerAdapter ha;
        do {
            if (!var2.hasNext()) {
                throw new ServletException("No adapter for handler [" + handler + "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
            }
    
            ha = (HandlerAdapter)var2.next();
            if (this.logger.isTraceEnabled()) {
                this.logger.trace("Testing handler adapter [" + ha + "]");
            }
        } while(!ha.supports(handler));
    
        return ha;
    }

回去到适配器之后,做handle处理,相关方法:

    mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
    
    org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter#handle
    
    public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        return ((Controller)handler).handleRequest(request, response);
    }

其中 handleRequest(request, response) 的实现方法就是我们自己写的 SimpleControl 中的 handleRequest 方法:

    @Override
    public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
        ModelAndView modelAndView = new ModelAndView("userView");
        modelAndView.addObject("name","ayang");
        return modelAndView;
    }

handleRequest 处理完成之后,就会返回一个 ModelAndView ,接下来会用到 resolveViewName ,由 viewName 来获取到 view ,然后 view 再进行解析,相关源码如下:

    org.springframework.web.servlet.DispatcherServlet#resolveViewName
    
    protected View resolveViewName(String viewName, Map<String, Object> model, Locale locale, HttpServletRequest request) throws Exception {
        Iterator var5 = this.viewResolvers.iterator();
    
        View view;
        do {
            if (!var5.hasNext()) {
                return null;
            }
    
            ViewResolver viewResolver = (ViewResolver)var5.next();
            view = viewResolver.resolveViewName(viewName, locale);
        } while(view == null);
    
        return view;
    }

整个 mvc 执行流程如下:

    dispatcherServlet -> handlermapping ->基于url查找handler -> handlerAdapter
    -> 基于handler找到adapter  -> 由adapter找到我们的 handler -> 执行业务处理返回 modelAndView
    -> viewResolver -> 基于viweName找到view -> 执行视图解析 -> 返回前端

欢迎留言交流:)