Springboot之mvc原理(一)-请求处理
篇幅较大,认真看我可能需要10分钟!
一
概述
springboot出现以后,我们搭建spring应用的复杂度大大降低,仅仅需要简单的注解和若干配置类就能构建简单的应用,这些都依赖于springboot默认集成了一整套的spring核心组件,比如在新版本的springboot的中,web和aop能力是完全不用配置和注解开始就能直接使用,这也是springboot设计和存在的初衷,尽可能大的程度上降低spring应用搭建和配置成本,将研发人员的主要精力尽可能投入在业务开发中。
前边讲到,springboot目前已经对web提供了原生支持,那么对springmvc的一整套流程也是无缝接入,本篇文章我们将着重讲述springboot mvc对请求处理的原理实现和源码分析。
二
原理&源码分析
1
请求处理流程
上图是spring mvc的请求处理流程,将请求处理分成了8个核心步骤,如果细分可能会有更多步骤,这里我们先简单分析一下每个步骤做的事情,细节的话后边源码分析会讲到:
2
原理解析
我们以tomcat容器为例,springboot应用启动后,浏览器发送请求会先经过tomcat,然后tomcat的执行引擎会寻找应用的Servlet实现并调用其service方法,但是如果我们通过xml配置文件或者配置类显式配置了DispatcherServlet并配置了init方法执行时机,那么应用启动后会先执行init方法进行初始化,如果没有显式配置DispatcherServlet,DispatcherServletConfiguration中默认暴露的DispatcherServlet是没有设置init方法执行时机的,那么应用启动后并不会执行其init方法,而是在应用第一次接收到请求的时候先执行init方法,再执行service方法,并且保证init只执行一次。
3
源码分析
springboot处理请求的核心类是DispatcherServlet,我们先看一下其类继承关系:
核心的继承关系是:
DispatcherServlet最终实现了Java定义的Servlet接口(和Jdbc一个套路,jdk官方提供接口规范,各大厂商自己实现),Servlet中比较核心的两个方法是init和service:
/**
* Called by the servlet container to indicate to a servlet that the servlet
* is being placed into service.
*
* <p>
* The servlet container calls the <code>init</code> method exactly once
* after instantiating the servlet. The <code>init</code> method must
* complete successfully before the servlet can receive any requests.
*
* <p>
* The servlet container cannot place the servlet into service if the
* <code>init</code> method
* <ol>
* <li>Throws a <code>ServletException</code>
* <li>Does not return within a time period defined by the Web server
* </ol>
*
*
* @param config
* a <code>ServletConfig</code> object containing the servlet's
* configuration and initialization parameters
*
* @exception ServletException
* if an exception has occurred that interferes with the
* servlet's normal operation
*
* @see UnavailableException
* @see #getServletConfig
*/
public void init(ServletConfig config) throws ServletException;
/**
* Called by the servlet container to allow the servlet to respond to a
* request.
*
* <p>
* This method is only called after the servlet's <code>init()</code> method
* has completed successfully.
*
* <p>
* The status code of the response always should be set for a servlet that
* throws or sends an error.
*
*
* <p>
* Servlets typically run inside multithreaded servlet containers that can
* handle multiple requests concurrently. Developers must be aware to
* synchronize access to any shared resources such as files, network
* connections, and as well as the servlet's class and instance variables.
* More information on multithreaded programming in Java is available in <a
* href
* ="http://java.sun.com/Series/Tutorial/java/threads/multithreaded.html">
* the Java tutorial on multi-threaded programming</a>.
*
*
* @param req
* the <code>ServletRequest</code> object that contains the
* client's request
*
* @param res
* the <code>ServletResponse</code> object that contains the
* servlet's response
*
* @exception ServletException
* if an exception occurs that interferes with the servlet's
* normal operation
*
* @exception IOException
* if an input or output exception occurs
*/
public void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException;
从方法签名以及注释来看,init和service的作用和执行时机如下:
我们前边有说过,servlet的service方法最终会调用到DispatcherServlet的doService方法,但是在此之前会执行init方法,会对DispatcherServlet做一些初始化准备工作,我们分别看一下两个核心方法的执行时序图:
从时序图中可以看出,init方法调用最终会为DispatcherServlet初始化准备9大组件供其使用:
servlet容器接收到请求后调用应用的servlet实现,最终会调用到DispatcherServlet的doDispatch方法,对于前边的准备工作不是本篇分析的主要内容,我们重点分析DispatcherServlet的doDispatch方法,看一下其核心实现:
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 {
//检查是否是文件上传请求,如果是转换成MultipartHttpServletRequest
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// Determine handler for the current request.
//获取Handler处理链
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 (logger.isDebugEnabled()) {
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;
}
// Actually invoke the handler.
//实际调用处理程序
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
applyDefaultViewName(processedRequest, mv);
//执行后置拦截器逻辑
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 other scenarios.
dispatchException = new NestedServletException("Handler dispatch failed", 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 processing failed", 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);
}
}
}
}
代码里边比较核心并且我们本篇需要关注的点已经加了简单的注释,分别是:
getHandler返回请求处理链,看一下实现:
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
if (this.handlerMappings != null) {
for (HandlerMapping hm : this.handlerMappings) {
if (logger.isTraceEnabled()) {
logger.trace(
"Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
}
HandlerExecutionChain handler = hm.getHandler(request);
if (handler != null) {
return handler;
}
}
}
return null;
}
先获取配置的HandlerMapping列表,springboot应用启动后会有7个HandlerMapping,分别是SimpleUrlHandlerMapping,BeanNameUrlHandlerMapping和RequestMappingHandlerMapping等,第一个是基于xml配置的,第二个需要自定义controller继承AbstractController或者实现Controller接口,第三个是支持基于配置的HandlerMapping,前两个由于在springboot轻量级简洁化开发模式主导情况下基本处于废弃或者濒临报废状态,在免配置注解驱动的springboot应用中RequestMappingHandlerMapping大发神威。代码中遍历配置的HandlerMapping列表,如果HandlerMapping能够返回Handler处理链则直接返回,否则继续遍历或者最终返回空,继续看HandlerMapping的getHandler实现:
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
Object handler = getHandlerInternal(request);
if (handler == null) {
handler = getDefaultHandler();
}
if (handler == null) {
return null;
}
// Bean name or resolved handler?
if (handler instanceof String) {
String handlerName = (String) handler;
handler = obtainApplicationContext().getBean(handlerName);
}
HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
if (CorsUtils.isCorsRequest(request)) {
CorsConfiguration globalConfig = this.globalCorsConfigSource.getCorsConfiguration(request);
CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
}
return executionChain;
}
该方法先根据请求(url)获取Handler(不再展开),如果没有获取到返回空(比如非法url),如果存在把匹配的拦截器(系统配置和自定义)添加到处理链中并返回。
我们接着看getHandlerAdapter实现:
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
Object handler = getHandlerInternal(request);
if (handler == null) {
handler = getDefaultHandler();
}
if (handler == null) {
return null;
}
// Bean name or resolved handler?
if (handler instanceof String) {
String handlerName = (String) handler;
handler = obtainApplicationContext().getBean(handlerName);
}
HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
if (CorsUtils.isCorsRequest(request)) {
CorsConfiguration globalConfig = this.globalCorsConfigSource.getCorsConfiguration(request);
CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
}
return executionChain;
}
获取系统配置的HandlerAdapter列表,分别是RequestMappingHandlerAdapter、HttpRequestHandlerAdapter和SimpleControllerHandlerAdapter,支持HttpMethod、HttpRequestHandler和Controller类型的Handler,前边讲到springboot应用使用基于注解支持的RequestMappingHandlerMapping,会将@RequestMapping注解的方法包装成HttpMethod,所以此处会返回RequestMappingHandlerAdapter。
继续看mappedHandler.applyPreHandle方法:
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
HandlerInterceptor[] interceptors = getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
for (int i = 0; i < interceptors.length; i++) {
HandlerInterceptor interceptor = interceptors[i];
if (!interceptor.preHandle(request, response, this.handler)) {
triggerAfterCompletion(request, response, null);
return false;
}
this.interceptorIndex = i;
}
}
return true;
}
该方法会获取所以符合条件的系统和自定义拦截器,并按照优先级执行其preHandle方法,如果结果返回false直接返回false给DispatcherServlet结束调用,比如我们对于权限拦截的注解就在此处执行。
接下来执行HandlerAdapter的handle方法就开始处理我们应用中编写的具体的业务逻辑,看一下代码实现:
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return handleInternal(request, response, (HandlerMethod) handler);
}
调用抽象类AbstractHandlerMethodAdapter的handle方法,接着调用子类RequestMappingHandlerMapping的handleInternal方法:
protected ModelAndView handleInternal(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ModelAndView mav;
checkRequest(request);
// Execute invokeHandlerMethod in synchronized block if required.
if (this.synchronizeOnSession) {
HttpSession session = request.getSession(false);
if (session != null) {
Object mutex = WebUtils.getSessionMutex(session);
synchronized (mutex) {
mav = invokeHandlerMethod(request, response, handlerMethod);
}
}
else {
// No HttpSession available -> no mutex necessary
mav = invokeHandlerMethod(request, response, handlerMethod);
}
}
else {
// No synchronization on session demanded at all...
mav = invokeHandlerMethod(request, response, handlerMethod);
}
if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
}
else {
prepareResponse(response);
}
}
return mav;
}
该方法调用内部方法invokeHandlerMethod并返回视图:
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ServletWebRequest webRequest = new ServletWebRequest(request, response);
try {
WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
if (this.argumentResolvers != null) {
invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
}
if (this.returnValueHandlers != null) {
invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
}
invocableMethod.setDataBinderFactory(binderFactory);
invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
modelFactory.initModel(webRequest, mavContainer, invocableMethod);
mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
asyncWebRequest.setTimeout(this.asyncRequestTimeout);
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.setTaskExecutor(this.taskExecutor);
asyncManager.setAsyncWebRequest(asyncWebRequest);
asyncManager.registerCallableInterceptors(this.callableInterceptors);
asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);
if (asyncManager.hasConcurrentResult()) {
Object result = asyncManager.getConcurrentResult();
mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
asyncManager.clearConcurrentResult();
if (logger.isDebugEnabled()) {
logger.debug("Found concurrent result value [" + result + "]");
}
invocableMethod = invocableMethod.wrapConcurrentResult(result);
}
invocableMethod.invokeAndHandle(webRequest, mavContainer);
if (asyncManager.isConcurrentHandlingStarted()) {
return null;
}
return getModelAndView(mavContainer, modelFactory, webRequest);
}
finally {
webRequest.requestCompleted();
}
}
从代码中可以看出,先根据HandlerMethod创建一个可执行的ServletInvocableHandlerMethod,然后调用invokeAndHandle执行逻辑,最后组装并返回视图模型:
接续回到DispatcherServlet主流程,接着看mappedHandler.applyPostHandle方法:
void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv)
throws Exception {
HandlerInterceptor[] interceptors = getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
for (int i = interceptors.length - 1; i >= 0; i--) {
HandlerInterceptor interceptor = interceptors[i];
interceptor.postHandle(request, response, this.handler, mv);
}
}
}
同前置拦截处理一样,先获取符合条件的拦截器,按照优先级倒序执行postHandle逻辑,和前置拦截器执行顺序刚好顺序相反:
最后,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.isDebugEnabled()) {
logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() +
"': assuming HandlerAdapter completed request handling");
}
}
if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
// Concurrent handling started during a forward
return;
}
if (mappedHandler != null) {
mappedHandler.triggerAfterCompletion(request, response, null);
}
}
具体的数据视图解析与返回,本篇不再做赘述,之前一篇文章《spring内容协商》有详细描述,借用一张时序图:
也就是CNVR作为一个代理视图解析器,将请求委托给具体的视图解析器处理并返回结果。
总结
篇幅有点大,并且分析的内容也比较多,但是认真分析并不是很难,简单总结一下,本篇重点讲述了springboot应用对http请求的处理过程以及从源码维度分析了DispatcherServlet的工作原理,但是这些都是在servlet容器启动之后的请求处理分析,请思考一个问题,为什么springboot应用启动后就能支持web能力,在启动过程中帮我们做了什么?后续系列将继续分析springboot对webmvc的自动配置和原生支持。
本文分享自 PersistentCoder 微信公众号,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文参与 腾讯云自媒体同步曝光计划 ,欢迎热爱写作的你一起参与!