8Spring MVC系列(7)-DispatcherServlet处理请求流程源码分析

2021/9/12 1:05:18

本文主要是介绍8Spring MVC系列(7)-DispatcherServlet处理请求流程源码分析,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

前言

在之前我们了解到了DispatcherServlet是如何初始化的,接下来我们了解下他是如何处理请求的呢?

Tomcat处理请求流程

首先,我们了解下Tomcat启动后是如何处理HTTP请求的?

文档来源

网络通信三要素

  • IP地址:计算机在网络上的唯一标示,通过ip找到通信所在的机器
  • 端口:端口是一台机器上不同程序的标示
  • 传输协议:双方都遵守的传输数据的格式

HTTP工作原理

HTTP协议是浏览器与服务器之间的数据传送协议。作为应用层协议,HTTP是基于TCP/IP协议来传递数据的(HTML文件、图片、查询结果等),HTTP协议不涉及数据包(Packet )传输,主要规定了客户端和服务器之间的通信格式。
在这里插入图片描述
从图上你可以看到,这个过程是:

  1. 用户通过浏览器进行了一个操作,比如输入网址并回车,或者是点击链接,接着浏览器获取了这个事件。
  2. 浏览器向服务端发出TCP连接请求。
  3. 服务程序接受浏览器的连接请求,并经过TCP三次握手建立连接。
  4. 浏览器将请求数据打包成一个HTTP协议格式的数据包。
  5. 浏览器将该数据包接入网络,数据包经过网络传输,最终达到端服务程序。
  6. 服务端程序拿到这个数据包后,同样以HTTP协议格式解包,获取到客户端的意图。
  7. 得知客户端意图后进行处理,比如提供静态文件或者调用服务端程序获得动态结果。
  8. 服务器将响应结果(可能是HTML或者图片等)按照HTTP协议格式打包。
  9. 服务器将响应数据包推入网络,数据包经过网络传输最终达到到浏览器。
  10. 浏览器拿到数据包后,以HTTP协议的格式解包,然后解析数据,假设这里的数据是HTML。
  11. 浏览器将HTML文件展示在页面上。

Tomcat作为一个Servlet服务器,在这个过程中主要是接受连接、解析请求数据、处理请求和发送响应。

Tomcat整体架构

1. Http服务器请求处理

浏览器发给服务端的是一个Http格式的请求,Http服务器收到这个请求后,需要调用服务端程序来处理,所谓的服务端程序就是你写的Java类,一般来说不同的请求需要由不同的Java类来处理。
在这里插入图片描述
图1,表示HTTP服务器直接调用具体业务类,它们是紧耦合的。

图2,HTTP服务器不直接调用业务类,而是把请求交给容器来处理,容器通过servlet接口调用业务类。因此servlet接口和servlet容器的出现,达到了HTTP服务器与业务类解耦的目的。而servlet接口和servlet容器这一整套规范叫作servlet规范。Tomcat按照servlet规范的要求实现了servlet容器,同时它们也具有HTTP服务器的功能。

2. Servlet容器工作流程

为了解耦,HTTP服务器不直接调用servlet,而是把请求交给servlet容器来处理。

当客户请求某个资源时,HTTP服务器会用一个servletRequest对象把客户的请求信息封装起来,然后调用servlet容器的service方法,Servlet容器拿到请求后,根据请求的URL和Servlet的映射关系,找到相应的servlet,如果servlet还没有被加载,就用反射机制创建这个servlet,并调用servlet的init方法来完成初始化,接着调用servlet的service方法来处理请求,把servletResponse对象返回给HTTP服务器,HTTP服务器会把响应发送给客户端。
在这里插入图片描述

3. Tomcat整体架构

我们知道如果要设计一个系统,首先是要了解需求,我们已经了解了Tomcat要实现两个核心功能︰

  • 处理socket连接,负责网络字节流与Request和Response对象的转化。

  • 加载和管理servlet ,以及具体处理Request请求。

因此Tomcat设计了两个核心组件连接器(connector )和容器( container )来分别做这两件事情。连接器负责对外交流,容器负责内部处理。

在这里插入图片描述

Tomcat请求处理流程

hadow_50,text_Q1NETiBA5LqR54Of5oiQ6ZuoY3Nkbg==,size_20,color_FFFFFF,t_70,g_se,x_16)

步骤如下:

  1. Connector组件Endpoint中的Acceptor监听客户端套接字连接并接收socket。
  2. 将连接交给线程池Executor处理,开始执行请求响应任务
  3. Processor组件读取消息报文,解析请求行、请求体、请求头,封装成Request对象。
  4. Mapper组件根据请求行的URL值和请求头的Host值匹配由哪个Host容器、Context容器、wrapper容器处理请求。
  5. coyoteAdaptor组件负责将connector组件和Engine容器关联起来,把生成的Request对象和响应对象Rsponse传递到Engine容器中,调用Pipeline。
  6. Engine容器的管道开始处理,管道中包含若干个valve、每个valve负责部分处理逻辑。执行完valve后会执行基础的valve–
    standardEnginevalve,负责调用Host容器的Pipeline。
  7. Host容器的管道开始处理,流程类似,最后执行Context容器的Pipeline.
  8. context容器的管道开始处理,流程类似,最后执行wrapper容器的pipeline.
  9. Wrapper容器的管道开始处理,流程类似,最后执行wrapper容器对应的servlet对象的处理方法。

DispatcherServlet处理请求流程

通过上面Tomcat处理流程分析,可以了解到,请求经过Tomcat一些列组件处理,再调用我们的servlet进行逻辑处理的。

1. 进入service()

之前分析了DispatcherServlet中初始化init的流程,当xServlet在处理请求时,会进入service方法。

DispatcherServlet中service进入的是父类FrameworkServlet中的service。

    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    	// 1. 获取请求方式
        HttpMethod httpMethod = HttpMethod.resolve(request.getMethod());
        // 2. 请求方式不为null,并且不是PATCH 请求,调用父类的HttpServlet的service方法
        if (httpMethod != HttpMethod.PATCH && httpMethod != null) {
            super.service(request, response);
        } else {
        	// 3. 处理请求
            this.processRequest(request, response);
        }

    }

这里测试的是GET请求,所以上述代码会进入HttpServlet.service方法,根据不同的请求方式执行不同的do方法,

    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String method = req.getMethod();
        long lastModified;
        // 1. 如果是GET
        if (method.equals("GET")) {
        	// 2. 获取最后修改时间
            lastModified = this.getLastModified(req);
            if (lastModified == -1L) {
                this.doGet(req, resp);
            } else {
            	// 3. 最后修改时不是-1
            	// If-Modified-Since是标准的HTTP请求头标签,在发送HTTP请求时,把浏览器端缓存页面的最后修改时间一起发到服务器去,服务器会把这个时间与服务器上实际文件的最后修改时间进行比较。
				// 如果时间一致,那么返回HTTP状态码304(不返回文件内容),客户端接到之后,就直接把本地缓存文件显示到浏览器中。
				//如果时间不一致,就返回HTTP状态码200和新的文件内容,客户端接到之后,会丢弃旧文件,把新文件缓存起来,并显示到浏览器中。
                long ifModifiedSince;
                try {
                    ifModifiedSince = req.getDateHeader("If-Modified-Since");
                } catch (IllegalArgumentException var9) {
                    ifModifiedSince = -1L;
                }

                if (ifModifiedSince < lastModified / 1000L * 1000L) {
                    this.maybeSetLastModified(resp, lastModified);
                    this.doGet(req, resp);
                } else {
                    resp.setStatus(304);
                }
            }
        } else if (method.equals("HEAD")) {
            lastModified = this.getLastModified(req);
            this.maybeSetLastModified(resp, lastModified);
            this.doHead(req, resp);
        } else if (method.equals("POST")) {
            this.doPost(req, resp);
        } else if (method.equals("PUT")) {
            this.doPut(req, resp);
        } else if (method.equals("DELETE")) {
            this.doDelete(req, resp);
        } else if (method.equals("OPTIONS")) {
            this.doOptions(req, resp);
        } else if (method.equals("TRACE")) {
            this.doTrace(req, resp);
        } else {
            String errMsg = lStrings.getString("http.method_not_implemented");
            Object[] errArgs = new Object[]{method};
            errMsg = MessageFormat.format(errMsg, errArgs);
            resp.sendError(501, errMsg);
        }

    }

2. 进入Doxx方法

上述进入service方法后,会根据不同的请求进入不同的Do方法,我们的GET请求会进入FrameworkServlet的doGet方法。

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

可以看到我们常用的请求方式,都会直接调用processRequest方法,只有doOptions、doTrace会做特殊处理。
在这里插入图片描述

FrameworkServlet的processRequest方法会对请求进行前置处理。

    protected final void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    	// 系统时间
        long startTime = System.currentTimeMillis();
        Throwable failureCause = null;
        // Local本地化 =》 zh_CN
        LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
        // 构建LocaleContext
        LocaleContext localeContext = this.buildLocaleContext(request);
        // 获取当前绑定到线程的RequestAttributes
        RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
        // 构建ServletRequestAttributes
        ServletRequestAttributes requestAttributes = this.buildRequestAttributes(request, response, previousAttributes);
        // 获取异步管理器
        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
        asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new FrameworkServlet.RequestBindingInterceptor());
        // 初始化ContextHolder
        this.initContextHolders(request, localeContext, requestAttributes);

        try {
        	// 调用doService
            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 {
        	// 重新设置ContextHolder
            this.resetContextHolders(request, previousLocaleContext, previousAttributes);
            if (requestAttributes != null) {
                requestAttributes.requestCompleted();
            }
			// 记录日志
            this.logResult(request, response, (Throwable)failureCause, asyncManager);
            // 发布事件 ContextHolder
            this.publishRequestHandledEvent(request, response, startTime, (Throwable)failureCause);
        }

    }

3. 进入doService方法

在processRequest中会调用 this.doService方法,进入到DispatcherServlet。

DispatcherServlet.doService主要负责像request域对象中设置一些属性。

protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
        this.logRequest(request);
        Map<String, Object> attributesSnapshot = null;
        // request中存在属性javax.servlet.include.request_uri
        if (WebUtils.isIncludeRequest(request)) {
            attributesSnapshot = new HashMap();
            Enumeration attrNames = request.getAttributeNames();

            label116:
            while(true) {
                String attrName;
                do {
                    if (!attrNames.hasMoreElements()) {
                        break label116;
                    }

                    attrName = (String)attrNames.nextElement();
                } while(!this.cleanupAfterInclude && !attrName.startsWith("org.springframework.web.servlet"));

                attributesSnapshot.put(attrName, request.getAttribute(attrName));
            }
        }
		// 设置属性org.springframework.web.servlet.DispatcherServlet.CONTEXT 
		// org.springframework.web.servlet.DispatcherServlet.THEME_SOURCE
		// org.springframework.web.servlet.DispatcherServlet.LOCALE_RESOLVER 
        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简单来说就是一个HashMap,用于数据保存
            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 previousRequestPath = null;
        if (this.parseRequestPath) {
            previousRequestPath = (RequestPath)request.getAttribute(ServletRequestPathUtils.PATH_ATTRIBUTE);
            ServletRequestPathUtils.parseAndCache(request);
        }

        try {
        	// 执行调度!!!
            this.doDispatch(request, response);
        } finally {
            if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted() && attributesSnapshot != null) {
                this.restoreAttributesAfterInclude(request, attributesSnapshot);
            }

            if (this.parseRequestPath) {
                ServletRequestPathUtils.setParsedRequestPath(previousRequestPath, request);
            }

        }

    }

4. 进入doDispatch方法开始调度

经过之前的一系列处理,终于进入DispatcherServlet的doDispatch开始对请求进行调度处理。

    protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        HttpServletRequest processedRequest = request;
        HandlerExecutionChain mappedHandler = null; // 执行链,包含当前拦截器、处理器(controller方法)、拦截器索引。
        boolean multipartRequestParsed = false;
        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

        try {
            try {
                ModelAndView mv = null;
                Object dispatchException = null;

                try {
                	// 1. 检查请求是否是multipart(即文件上传)
                    processedRequest = this.checkMultipart(request);
                    multipartRequestParsed = processedRequest != request;
                    // 2. 通过handermapping映射获取HandlerExecutionChain(处理链中包括了interceptor的前置和后置方法)
                    mappedHandler = this.getHandler(processedRequest);
                    if (mappedHandler == null) {
                        this.noHandlerFound(processedRequest, response);
                        return;
                    }
					// 3. 根据处理器(handler及HandlerExecutionChain)获取处理器适配器(处理器适配器是为了提供统一接口进行后续处理,从而支持多种类型的处理器)
                    HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
                    String method = request.getMethod();
                    boolean isGet = HttpMethod.GET.matches(method);
                    if (isGet || HttpMethod.HEAD.matches(method)) {
                        long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
                        if ((new ServletWebRequest(request, response)).checkNotModified(lastModified) && isGet) {
                            return;
                        }
                    }
					// 4. 调用拦截器的PreHandle方法,循环拦截器。
                    if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                        return;
                    }
					// 5. 处理器适配器执行控制器方法。也就是controller中的方法,处理参数,类型转换等。
                    mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
                    if (asyncManager.isConcurrentHandlingStarted()) {
                        return;
                    }

                    this.applyDefaultViewName(processedRequest, mv);
                    // 6. 调用拦截器的PostHandle。
                    mappedHandler.applyPostHandle(processedRequest, response, mv);
                } catch (Exception var20) {
                    dispatchException = var20;
                } catch (Throwable var21) {
                    dispatchException = new NestedServletException("Handler dispatch failed", var21);
                }
				// 7. 后续处理
                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);
            }

        }
    }

处理器、拦截器处理完成后,最终进入到processDispatchResult进行后续处理。

    private void processDispatchResult(HttpServletRequest request, HttpServletResponse response, @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv, @Nullable Exception exception) throws Exception {
        boolean errorView = false;
        // 1. 如果有异常,processHandlerException处理异常
        if (exception != null) {
            if (exception instanceof ModelAndViewDefiningException) {
                this.logger.debug("ModelAndViewDefiningException encountered", exception);
                mv = ((ModelAndViewDefiningException)exception).getModelAndView();
            } else {
                Object handler = mappedHandler != null ? mappedHandler.getHandler() : null;
                mv = this.processHandlerException(request, response, handler, exception);
                errorView = mv != null;
            }
        }

        if (mv != null && !mv.wasCleared()) {
            this.render(mv, request, response);
            if (errorView) {
                WebUtils.clearErrorRequestAttributes(request);
            }
        } else if (this.logger.isTraceEnabled()) {
            this.logger.trace("No view rendering, null ModelAndView returned.");
        }

        if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
            if (mappedHandler != null) {
            	// 3. afterCompletion
                mappedHandler.triggerAfterCompletion(request, response, (Exception)null);
            }
        }
    }

最终,执行一些finally方法,请求处理流程就结束了,具体某些处理细节,后续会再慢慢介绍。



这篇关于8Spring MVC系列(7)-DispatcherServlet处理请求流程源码分析的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程