zoukankan      html  css  js  c++  java
  • HTTP请求处理流程-SpringMvc

    1、在SpringMVC的http请求处理过程中,包括了前端控制器(DispatcherServlet)、处理映射器(HandlerMapping)、处理适配器(HandlerAdapter)、处理器((Handler)Controller)、视图解析器(ViewReslover)、视图(View)这六大主要对象。他们负责对http请求做处理,具体流程如下图。

    第一步:前端控制器dispatcher接受请求

        Client---url--->Dispatcher

    第二步:前端控制器去发起handler映射查找请求

        Dispatcher---HttpServletRequest---> HandlerMapping

    第三步:处理器映射器查找hanlder并返回HandlerExetuionChain

         Dispatcher <---HandlerExeutionChain---HandlerMapping

    第四步:前端控制器发起请求处理器适配器请求执行

      Dispatcher---Handler---> HandlerAdapter

    第五步:处理器适配器去调用handler执行

    HandlerAdapter---HttpServletRequest> Handler(Controller)

    第六步:处理器处理后返回ModelAndView给HandlerAdapter

    HandlerAdapter <---ModelAndView---Handler(Controller)

    第七步:处理器适配器将ModelAndView返回给前端控制器

    Dispatcher <---ModelAndView---HandlerAdapter

    第八步:前端控制器请求视图解析器解析ModelAndView

    Dispatcher---ModelAndView---> ViewReslover

    第九步:视图解析器解析视图后返回视图View给前端控制器

    Dispatcher <---View---ViewReslover

    第十步:前端控制器请求视图要求渲染视图

    Dispatcher--->View--->render

    第十一步:前端控制器返回响应

    Response <---Dispatcher

    源码探秘

      第一步接受请求:

    我们可以来看看DispatcherServlet的继承结构


    其实DispatcherServlet能处理请求是因为HttpServlet类的service方法,而HttpServlet又来自Servlet接口定义的规范。


    可以看到抽象类HttpServlet实现了接口Servlet的service方法,根据请求类型不同执行了不同的方法(doGet,doPost)


    当请进来后,由HttpServlet的子类FrameworkServlet重写的service方法执行请求,可以看到437行子类调用了父类的service方法,然后在父类执行doGet之类的方法时,由于子类FrameworkServlet重写了父类方法,交由子类执行,所以进到了我的doGet断点里面,它调用了处理请求方法。

    接下来我们看看ProcessRequest方法的源码

     protected final void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            long startTime = System.currentTimeMillis();
            Throwable failureCause = null;
            LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
            LocaleContext localeContext = this.buildLocaleContext(request);
            RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
            ServletRequestAttributes requestAttributes = this.buildRequestAttributes(request, response, previousAttributes);
            WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
            asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new FrameworkServlet.RequestBindingInterceptor(null));
            this.initContextHolders(request, localeContext, requestAttributes);
    
            try {
                this.doService(request, response);
            } catch (IOException | ServletException var16) {
                failureCause = var16;
                throw var16;
            } catch (Throwable var17) {
                failureCause = var17;
                throw new NestedServletException("Request processing failed", var17);
            } finally {
                this.resetContextHolders(request, previousLocaleContext, previousAttributes);
                if (requestAttributes != null) {
                    requestAttributes.requestCompleted();
                }
    
                this.logResult(request, response, (Throwable)failureCause, asyncManager);
                this.publishRequestHandledEvent(request, response, startTime, (Throwable)failureCause);
            }
    
        }

    前面一系列初始化工作我们先不管,看看重要的部分,try里面的doService方法


    跟踪进去看了一下,由于它是抽象方法,所以会由子类实现和执行,也就是我们的DispatchServlet类了


    老规矩,先贴上源码,它是DispatchServlet的doService方法--------------------------------------------------------------------------------------------------------------

     protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
            this.logRequest(request);
            Map<String, Object> attributesSnapshot = null;
            if (WebUtils.isIncludeRequest(request)) {
                attributesSnapshot = new HashMap();
                Enumeration attrNames = request.getAttributeNames();
    
                label95:
                while(true) {
                    String attrName;
                    do {
                        if (!attrNames.hasMoreElements()) {
                            break label95;
                        }
    
                        attrName = (String)attrNames.nextElement();
                    } while(!this.cleanupAfterInclude && !attrName.startsWith("org.springframework.web.servlet"));
    
                    attributesSnapshot.put(attrName, request.getAttribute(attrName));
                }
            }
    
            request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.getWebApplicationContext());
            request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
            request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
            request.setAttribute(THEME_SOURCE_ATTRIBUTE, this.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);
            }
    
            try {
                this.doDispatch(request, response);
            } finally {
                if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted() && attributesSnapshot != null) {
                    this.restoreAttributesAfterInclude(request, attributesSnapshot);
                }
    
            }
    
        }

    所以第一步也就完成了,第一步的任务就是走进这里来。

    第二步:前端控制器去发起handler映射查找请求

    Dispatcher---HttpServletRequest---> HandlerMapping

    上面的源码中主要工作就是给request实例设置一系列参数,要注意的就是doDispatch方法,这里面就是mvc的核心了,前面第一张交互图里面的流程都是在这里实现的。


    可以看到,通过HttpRequestServlet作为参数请求handlerMapping

    第三步:处理器映射器查找hanlder并返回HandlerExetuionChain

         Dispatcher <---HandlerExeutionChain---HandlerMapping

    可以看到上图中返回了mappedHandler变量,就是HandlerExtuceChain类型


    可以看到,已经找到并返回了我们的HomeController处理器(Hanlder)

    第四步:前端控制器发起请求处理器适配器请求执行

      Dispatcher---Handler---> HandlerAdapter


    从508行可以看到,适配器传入handler对象和reaquest等信息,执行handler()方法

    第五步:处理器适配器去调用handler执行

    HandlerAdapter---HttpServletRequest> Handler(Controller)


    从这里可以看到,调用的handlerInternal是个抽象方法,会调用子类的实现方法,子类由RequestMappingHandlerAdapter实现,这个类也是我们经常在xml里面配置的类


    通过invokeHandlerMethod方法执行进到controller里面


    方法执行后返回我们的index


    第六步:处理器处理后返回ModelAndView给HandlerAdapter

    HandlerAdapter <---ModelAndView---Handler(Controller)

    通过调用invokeHandlerMethod方法返回ModelAndView


    第七步:处理器适配器将ModelAndView返回给前端控制器

    Dispatcher <---ModelAndView---HandlerAdapter


    第八步:前端控制器请求视图解析器解析ModelAndView

    Dispatcher---ModelAndView---> ViewReslover


    第九步:视图解析器解析视图后返回视图View给前端控制器

    Dispatcher <---View---ViewReslover


    可以看到,返回的视图,url指向index.jsp页面

    第十步:前端控制器请求视图要求渲染视图

    Dispatcher--->View--->render

    如果View对象不为空,将会调用render方法渲染


    如果返回的是json对象,属于接口的,是不会走这里的


    此时会找对应的视图解析器去渲染


    里面其实也没干啥,就做了个跳转,到jsp页面去绑定数据


    第十一步:前端控制器返回响应

    Response <---Dispatcher


    到这里也就基本上完了。


    处理请求完成后做了个重置工作,然后发布一个事件,你可以选择监听这个事件,做相应处理。

    再看看response里面


    这个就是我们页面上的内容了。

  • 相关阅读:
    SQL Serever学习16——索引,触发器,数据库维护
    微信WeUI基础
    SQL Serever学习15——进阶
    SQL Serever学习14——存储过程和触发器
    微信WeUI入门2
    微信WeUI入门
    数据库—索引
    数据库—四种存储引擎
    视图
    事务
  • 原文地址:https://www.cnblogs.com/raorao1994/p/10569520.html
Copyright © 2011-2022 走看看