Spring WebMVC

Spring WebMVC

Spring 接受前端请求的处理过程,根据 SpringBoot 2.4.2 版本。

doService

放入各种属性到 request 对象上,然后交给 doDispatch 进行代理转发

public class DispatcherServlet extends FrameworkServlet {
    
    protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
        // ...
        request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
		request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
		request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
        // ...

        doDispatch(request, response);
    }

}

doDispatcher

public class DispatcherServlet extends FrameworkServlet {
    protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        // 用哪个 Handler 链处理请求
        mappedHandler = getHandler(processedRequest);

        // 用哪个 Handler adapter 处理请求
        HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

        // 处理 last-modified 头
        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;
        }

        // 开始实际处理请求
        mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

        // 请求拦截器
        mappedHandler.applyPostHandle(processedRequest, response, mv);

        // 处理请求的结果
        processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
    }
}

getHandler

// DispatcherServlet.java
@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;
}

handlerMappings 有哪些:

找到最匹配的 HandlerExecutionChain 以后,这个类内部的 handler 指向的就是 XXXController 这个类。

getHandlerAdapter

protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
    if (this.handlerAdapters != null) {
        for (HandlerAdapter adapter : this.handlerAdapters) {
            if (adapter.supports(handler)) {
                return adapter;
            }
        }
    }

    throw new ServletException("No adapter for handler [" + handler +
            "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}

handlerAdapters 有哪些:

处理 Last-Modified

如果是 GET 或者 HEAD 请求,检查是否是携带了 last-modified 头:

// ServletWebRequest.java
public boolean checkNotModified(@Nullable String etag, long lastModifiedTimestamp) {
    HttpServletResponse response = this.getResponse();
    if (!this.notModified && (response == null || HttpStatus.OK.value() == response.getStatus())) {
        if (this.validateIfUnmodifiedSince(lastModifiedTimestamp)) {
            if (this.notModified && response != null) {
                response.setStatus(HttpStatus.PRECONDITION_FAILED.value());
            }

            return this.notModified;
        } else {
            boolean validated = this.validateIfNoneMatch(etag);
            if (!validated) {
                this.validateIfModifiedSince(lastModifiedTimestamp);
            }

            if (response != null) {
                boolean isHttpGetOrHead = SAFE_METHODS.contains(this.getRequest().getMethod());
                if (this.notModified) {
                    response.setStatus(isHttpGetOrHead ? HttpStatus.NOT_MODIFIED.value() : HttpStatus.PRECONDITION_FAILED.value());
                }

                if (isHttpGetOrHead) {
                    if (lastModifiedTimestamp > 0L && this.parseDateValue(response.getHeader("Last-Modified")) == -1L) {
                        response.setDateHeader("Last-Modified", lastModifiedTimestamp);
                    }

                    if (StringUtils.hasLength(etag) && response.getHeader("ETag") == null) {
                        response.setHeader("ETag", this.padEtagIfNecessary(etag));
                    }
                }
            }

            return this.notModified;
        }
    } else {
        return this.notModified;
    }
}

检查 Last-Modified 的处理过程如下:

(1) 自上次某个修改时间以来是否没有变化过

查看 If-Unmodified-Since 头:

private boolean validateIfUnmodifiedSince(long lastModifiedTimestamp) {
    if (lastModifiedTimestamp < 0L) {
        return false;
    } else {
        long ifUnmodifiedSince = this.parseDateHeader("If-Unmodified-Since");
        if (ifUnmodifiedSince == -1L) {
            return false;
        } else {
            this.notModified = ifUnmodifiedSince < lastModifiedTimestamp / 1000L * 1000L;
            return true;
        }
    }
}

(2) 是否 Non-Match

上述如果不匹配,那么就会检查 etagIf-None-Match 是否匹配:

private boolean validateIfNoneMatch(@Nullable String etag) {
    if (!StringUtils.hasLength(etag)) {
        return false;
    } else {
        Enumeration ifNoneMatch;
        try {
            ifNoneMatch = this.getRequest().getHeaders("If-None-Match");
        } catch (IllegalArgumentException var5) {
            return false;
        }

        if (!ifNoneMatch.hasMoreElements()) {
            return false;
        } else {
            etag = this.padEtagIfNecessary(etag);
            if (etag.startsWith("W/")) {
                etag = etag.substring(2);
            }

            while(true) {
                while(ifNoneMatch.hasMoreElements()) {
                    String clientETags = (String)ifNoneMatch.nextElement();
                    Matcher etagMatcher = ETAG_HEADER_VALUE_PATTERN.matcher(clientETags);

                    while(etagMatcher.find()) {
                        if (StringUtils.hasLength(etagMatcher.group()) && etag.equals(etagMatcher.group(3))) {
                            this.notModified = true;
                            break;
                        }
                    }
                }

                return true;
            }
        }
    }
}

(3) 是否自某个时间开始后修改了

private boolean validateIfModifiedSince(long lastModifiedTimestamp) {
    if (lastModifiedTimestamp < 0L) {
        return false;
    } else {
        long ifModifiedSince = this.parseDateHeader("If-Modified-Since");
        if (ifModifiedSince == -1L) {
            return false;
        } else {
            this.notModified = ifModifiedSince >= lastModifiedTimestamp / 1000L * 1000L;
            return true;
        }
    }
}

请求拦截器

请求拦截器在真正处理请求前进行拦截器的 preHandlepostHandle 处理:

boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
    for (int i = 0; i < this.interceptorList.size(); i++) {
        HandlerInterceptor interceptor = this.interceptorList.get(i);
        if (!interceptor.preHandle(request, response, this.handler)) {
            triggerAfterCompletion(request, response, null);
            return false;
        }
        this.interceptorIndex = i;
    }
    return true;
}

void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv)
        throws Exception {

    for (int i = this.interceptorList.size() - 1; i >= 0; i--) {
        HandlerInterceptor interceptor = this.interceptorList.get(i);
        interceptor.postHandle(request, response, this.handler, mv);
    }
}

处理结果

private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
			@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
			@Nullable Exception exception) throws Exception {
    if (exception != null) {
    }

    if (mv != null && !mv.wasCleared()) {
        render(mv, request, response);
    }
}

render 会渲染 ModelAndView

protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
    String viewName = mv.getViewName();
    View view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
    view.render(mv.getModelInternal(), request, response);
}