首页 > 其他分享 >14. SpringMVC执行流程

14. SpringMVC执行流程

时间:2023-06-21 11:34:47浏览次数:37  
标签:14 SpringMVC wac 流程 request mappedHandler context null response

14.1、SpringMVC 常用组件

  • DispatcherServlet:前端控制器,不需要工程师开发,由框架提供

作用:统一处理请求和响应,整个流程控制的中心,由它调用其它组件处理用户的请求

  • HandlerMapping:处理器映射器,不需要工程师开发,由框架提供

作用:根据请求的 url、method 等信息查找 Handler,即控制器方法

  • Handler:处理器,需要工程师开发

作用:在 DispatcherServlet 的控制下 Handler 对具体的用户请求进行处理

  • HandlerAdapter:**处理器适配器**,不需要工程师开发,由框架提供

作用:通过 HandlerAdapter 对处理器(控制器方法)进行执行

  • ViewResolver:视图解析器,不需要工程师开发,由框架提供

作用:进行视图解析,得到相应的视图,例如:ThymeleafView、InternalResourceView、

RedirectView

  • View:视图

作用:将模型数据通过页面展示给用户

14.2、DispatcherServlet 初始化过程

DispatcherServlet 本质上是一个 Servlet,所以天然的遵循 Servlet 的生命周期。所以宏观上是 Servlet 生命周期来进行调度。

image

① 初始化 WebApplicationContext

所在类:org.springframework.web.servlet.FrameworkServlet

protected WebApplicationContext initWebApplicationContext() {
    WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext());
    WebApplicationContext wac = null;
    if (this.webApplicationContext != null) {
        // A context instance was injected at construction time -> use it
        wac = this.webApplicationContext;
        if (wac instanceof ConfigurableWebApplicationContext) {
            ConfigurableWebApplicationContext cwac =(ConfigurableWebApplicationContext) wac;
            if (!cwac.isActive()) {
                // The context has not yet been refreshed -> provide services such as
                    // setting the parent context, setting the application context id, etc
                    if (cwac.getParent() == null) {
                        // The context instance was injected without an explicit parent -> set
                            // the root application context (if any; may be null) as the parent
                            cwac.setParent(rootContext);
                    }
                configureAndRefreshWebApplicationContext(cwac);
            }
        }
    }
    if (wac == null) {
        // No context instance was injected at construction time -> see if one
        // has been registered in the servlet context. If one exists, it is assumed
            // that the parent context (if any) has already been set and that the
            // user has performed any initialization such as setting the context id
            wac = findWebApplicationContext();
    }
    if (wac == null) {
        // No context instance is defined for this servlet -> create a local one
        // 创建WebApplicationContext
        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) {
            // 刷新WebApplicationContext
            onRefresh(wac);
        }
    }
    if (this.publishContext) {
        // Publish the context as a servlet context attribute.
        // 将IOC容器在应用域共享
        String attrName = getServletContextAttributeName();
        getServletContext().setAttribute(attrName, wac);
    }
    return wac;
}

② 创建 WebApplicationContext

所在类:org.springframework.web.servlet.FrameworkServlet

protected WebApplicationContext createWebApplicationContext(@Nullable ApplicationContext parent) {
    Class<?> contextClass = getContextClass();
    if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass))
    {
        throw new ApplicationContextException("Fatal initialization error in servlet with name '" +getServletName() +
                                              "': custom WebApplicationContext class [" + contextClass.getName() +
                                              "] is not of type ConfigurableWebApplicationContext");
    }
    // 通过反射创建 IOC 容器对象
    ConfigurableWebApplicationContext wac = (ConfigurableWebApplicationContext)BeanUtils.instantiateClass(contextClass);
    wac.setEnvironment(getEnvironment());
    // 设置父容器
    wac.setParent(parent);
    String configLocation = getContextConfigLocation();
    if (configLocation != null) {
        wac.setConfigLocation(configLocation);
    }
    configureAndRefreshWebApplicationContext(wac);
    return wac;
}

③DispatcherServlet 初始化策略

FrameworkServlet 创建 WebApplicationContext 后,刷新容器,调用 onRefresh(wac),此方法在

DispatcherServlet 中进行了重写,调用了 initStrategies(context)方法,初始化策略,即初始化

DispatcherServlet 的各个组件

所在类:org.springframework.web.servlet.DispatcherServlet

protected void initStrategies(ApplicationContext context) {
    initMultipartResolver(context);
    initLocaleResolver(context);
    initThemeResolver(context);
    initHandlerMappings(context);
    initHandlerAdapters(context);
    initHandlerExceptionResolvers(context);
    initRequestToViewNameTranslator(context);
    initViewResolvers(context);
    initFlashMapManager(context);
}

14.3、DispatcherServlet 调用组件处理请求

①processRequest()

FrameworkServlet 重写 HttpServlet 中的 service()和 doXxx(),这些方法中调用了

processRequest(request, response)

所在类:org.springframework.web.servlet.FrameworkServlet

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 {
        // 执行服务,doService()是一个抽象方法,在DispatcherServlet中进行了重写
        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);
    }
}

②doService()

所在类:org.springframework.web.servlet.DispatcherServlet

@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 requestPath = null;
    if (this.parseRequestPath && !ServletRequestPathUtils.hasParsedRequestPath(request)) {
        requestPath = ServletRequestPathUtils.parseAndCache(request);
    }
    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);
            }
        }
        if (requestPath != null) {
            ServletRequestPathUtils.clearParsedRequestPath(request);
        }
    }
}

③doDispatch()

所在类: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 {
        ModelAndView mv = null;
        Exception dispatchException = null;
        try {
            processedRequest = checkMultipart(request);
            multipartRequestParsed = (processedRequest != request);
            // Determine handler for the current request.
            /*
                mappedHandler:调用链
                包含handler、interceptorList、interceptorIndex
                handler:浏览器发送的请求所匹配的控制器方法
                interceptorList:处理控制器方法的所有拦截器集合
                interceptorIndex:拦截器索引,控制拦截器afterCompletion()的执行
             */
            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;
                }
            }
            // 调用拦截器的preHandle()
            if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                return;
            }
            // Actually invoke the handler.
            // 由处理器适配器调用具体的控制器方法,最终获得ModelAndView对象
            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 otherscenarios.
            dispatchException = new NestedServletException("Handler dispatchfailed", 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 processingfailed",
                                                                                                    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);
            }
        }
    }
}                                                          

④processDispatchResult()

private void processDispatchResult(HttpServletRequest request,HttpServletResponse response,@Nullable HandlerExecutionChain
                                   mappedHandler, @Nullable ModelAndView mv,@Nullable Exception exception) throws Exception {
    boolean errorView = false;
    if (exception != null) {
        if (exception instanceof ModelAndViewDefiningException) {
            logger.debug("ModelAndViewDefiningException encountered",exception);
            mv = ((ModelAndViewDefiningException) exception).getModelAndView();
        }
        else {
            Object handler = (mappedHandler != null ? mappedHandler.getHandler(): null);
            mv = processHandlerException(request, response, handler, exception);
            errorView = (mv != null);
        }
    }
    // Did the handler return a view to render?
    if (mv != null && !mv.wasCleared()) {
        // 处理模型数据和渲染视图
        render(mv, request, response);
        if (errorView) {
            WebUtils.clearErrorRequestAttributes(request);
        }
    }
    else {
        if (logger.isTraceEnabled()) {
            logger.trace("No view rendering, null ModelAndView returned.");
        }
    }
    if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
        // Concurrent handling started during a forward
        return;
    }
    if (mappedHandler != null) {
        // Exception (if any) is already handled..
        // 调用拦截器的afterCompletion()
        mappedHandler.triggerAfterCompletion(request, response, null);
    }
}                                  

14.4、SpringMVC 的执行流程

\1) 用户向服务器发送请求,请求被 SpringMVC 前端控制器 DispatcherServlet 捕获。

\2) DispatcherServlet 对请求 URL 进行解析,得到请求资源标识符(URI),判断请求 URI 对应的映射:

a) 不存在

i. 再判断是否配置了 mvc:default-servlet-handler

ii. 如果没配置,则控制台报映射查找不到,客户端展示 404 错误

​​image​​

iii. 如果有配置,则访问目标资源(一般为静态资源,如:JS,CSS,HTML),找不到客户端也会展示 404

错误

image

b) 存在则执行下面的流程

\3) 根据该 URI,调用 HandlerMapping 获得该 Handler 配置的所有相关的对象(包括 Handler 对象以及

Handler 对象对应的拦截器),最后以 HandlerExecutionChain 执行链对象的形式返回。

\4) DispatcherServlet 根据获得的 Handler,选择一个合适的 HandlerAdapter。

\5) 如果成功获得 HandlerAdapter,此时将开始执行拦截器的 preHandler(…)方法【正向】

\6) 提取 Request 中的模型数据,填充 Handler 入参,开始执行 Handler(Controller)方法,处理请求。

在填充 Handler 的入参过程中,根据你的配置,Spring 将帮你做一些额外的工作:

a) HttpMessageConveter: 将请求消息(如 Json、xml 等数据)转换成一个对象,将对象转换为指定

的响应信息

b) 数据转换:对请求消息进行数据转换。如 String 转换成 Integer、Double 等

c) 数据格式化:对请求消息进行数据格式化。 如将字符串转换成格式化数字或格式化日期等

d) 数据验证: 验证数据的有效性(长度、格式等),验证结果存储到 BindingResult 或 Error 中

\7) Handler 执行完成后,向 DispatcherServlet 返回一个 ModelAndView 对象。

\8) 此时将开始执行拦截器的 postHandle(...)方法【逆向】。

\9) 根据返回的 ModelAndView(此时会判断是否存在异常:如果存在异常,则执行

HandlerExceptionResolver 进行异常处理)选择一个适合的 ViewResolver 进行视图解析,根据 Model

和 View,来渲染视图。

\10) 渲染视图完毕执行拦截器的 afterCompletion(…)方法【逆向】。

\11) 将渲染结果返回给客户端。

标签:14,SpringMVC,wac,流程,request,mappedHandler,context,null,response
From: https://www.cnblogs.com/NorthPoet/p/17495854.html

相关文章

  • 13. 注解配置SpringMVC
    使用配置类和注解代替web.xml和SpringMVC配置文件的功能13.1、创建初始化类,代替web.xml在Servlet3.0环境中,容器会在类路径中查找实现javax.servlet.ServletContainerInitializer接口的类,如果找到的话就用它来配置Servlet容器。Spring提供了这个接口的实现,名为SpringServletCo......
  • odoo14 ,实现主从表的连动
    需求:主表中的布尔字段的值。影响从表中字段的显示与否。 上代码<!--PPS样图片--><fieldname="pps_img"widget="image"attrs="{'column_invisible':[('parent.is_pps','=',False)]}&quo......
  • XXL-job开源框架相关的源码流程解析。
    XXL-job框架是一个分布式的定时任务框架。他简单快捷。配置方便。而且用途广泛。所以他的源码非常值得一看。对于我来说。其中其自写的RPC框架。以及处理发布多个定时任务的高并发处理。是我打开微服务的大门。这是一篇xxl-job源码的解析与流程分析。比较偏口语化。在这篇随笔中......
  • 3、SpringMVC
    1、简介1.1、mvc 1.2、SpringMVC基于原生的Servlet 2、使用引用......
  • 141. 环形链表 及其相关
    141.环形链表1.哈希表法:将节点依次加入set,如果有重复就是有环。publicclassSolution{publicbooleanhasCycle(ListNodehead){Set<ListNode>set=newHashSet<ListNode>();while(head!=null){if(!set.add(head)){......
  • EP3C40F484C8N+cyusb3014 该板子之前批量过,现在没有板子了,只有完整的开发资料。
    EP3C40F484C8N+cyusb3014该板子之前批量过,现在没有板子了,只有完整的开发资料。包含FPGA源码,usb源码。资料里有原理图和pcbID:5730605186874401......
  • 西门子S7-1200PLC 四轴定位控制程序(自动螺丝机) 程序是基于S7-1200 PLC (CPU 1214C ),
    西门子S7-1200PLC四轴定位控制程序(自动螺丝机)程序是基于S7-1200PLC(CPU1214C),博途V13SP1编写。程序中利用TOAXIS运动控制指令编写4轴定位程序,利用易福门相机视觉专用功能块(FB1FB2SCL高级语言)与PLC以太网通信,采集相机坐标位置参数。ID:1635602376517476......
  • 【React工作记录一百一十六】前端小知识点扫盲笔记记录14
    前言我是歌谣放弃很容易但是坚持一定很酷微信公众号关注前端小歌谣带你进入前端巅峰交流群今天继续对前端知识的小结根据每个元素i属性进行排序<!DOCTYPEhtml><htmllang="en"> <head> <metacharset="UTF-8"/> <metahttp-equiv="X-UA-Compatible"content="IE=edge&......
  • SpringMVC中接收前端传递的参数,设置了编码过滤器filter,但在控制台中还是出现乱码问题
    SpringMVC中接收前端传递的参数,设置了编码过滤器filter,但在控制台中还是出现乱码问题。 在SpringMVC中遇到乱码问题不要慌,先配个SpringMVC的自带编码过滤器试试 <filter><filter-name>CharacterEncodingFilter</filter-name><filter-class>org.spr......
  • iOS开发笔记 - App上架流程(视频分享)
    具体的文档可以看一下我的《iOS开发笔记-上线流程》iOS项目上线流程视频百度云盘分享下面是一些相关的官方文档:https://developer.apple.com/app-store/review/guidelines/-项目审核指南http://www.apple.com/legal/intellectual-property/guidelinesfor3rdparties.htmlhttps......