当前位置:   article > 正文

SpringMVC的工作原理及底层剖析,你值得一看_springmvc底层原理

springmvc底层原理

今天分析的是MVC在工作的时候底层干了些啥,仅仅是工作的时后,就是从DispatcherServlet接受到一个请求开始
简单分析一下这个流程
在这里插入图片描述

1.DispatcherServle表示前端控制器,是整个springMVC的控制中心,用户发出请求,
1.1 DispatcherServle接受拦请求并拦截,
假设请求的url为:http://localhost:8080/SpringMVC/hello
如上url拆分为三部分
http://localhost:8080服务器域名
SpringMVC部署在服务器上的web站点
hello表示控制器
通过分析,如上url表示的意思为:请求位于服务器locahost:8080上的SpringMVC站点的Hello控制器
1.2 HandlerMapping为处理器映射,DispatcherServle自行调用HandlerMapping,HandlerMapping根据url查找Handler
1.3 HandlerExecution表示具体的handler,其主要作用就是根据url查找控制器,如上url被查找控制器为Hello
1.4 HandlerExecution将解析后的数据传递给DispatcherServle,如解析控制器映射等
1.5 HandlerAdapter表示处理器适配器,其按照特定的规则去执行Handler
1.6 Handler让具体的controller执行
1.7 controller将具体的执行信息返回HandlerAdapter,如Model和View
1.8 HandlerAdapter将试图逻辑命或模型传递给DispatcherServle
1.9 DispatcherServle调用试图解析器,ViewEsolver来解析HandlerAdapter传递的逻辑试图名
1.10 视图解析器将解析的逻辑视图名传给DispatcherServle
1.11 DispatcherServlet根据试图解析器解析的试图结果,掉欧阳那个具体的试图
1.12 呈现给用户

View是一个接口,他的实现类支持很多种类型
接下来看看底层代码,每一步具体都干了什么
首先在web.xml中找到如下代码

<servlet>
	<servlet-name>dispatcher</servlet-name>
		<servlet-class>
			org.springframework.web.servlet.DispatcherServlet
		</servlet-class>
	<load-on-startup>1</load-on-startup>
</servlet>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

然后按住ctrl,鼠标左键点击DispatcherServlet进到底层
进来之后不要迷茫,直接Ctrl+f搜索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 {          //检查请求是否是multipart(即文件上传),若是进行相关处理
                processedRequest = checkMultipart(request);
                multipartRequestParsed = (processedRequest != request);

                          //通过handermapping映射获取HandlerExecutionChain(处理链中包括了interceptor的前置和后置方法)          // Determine handler for the current request.
                mappedHandler = getHandler(processedRequest);
                if (mappedHandler == null) {
                    noHandlerFound(processedRequest, response);
                    return;
                }
          //根据处理器(handler及HandlerExecutionChain)获取处理器适配器(处理器适配器是为了提供统一接口进行后续处理,从而支持多种类型的处理器)
                // 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;
                    }
                }
          //执行chain中拦截器附加的预处理方法,即preHandle方法                  
                if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                    return;
                }
          //适配器统一执行handle方法(适配器统一接口的作用),此处是真正处理业务逻辑的地方
                // Actually invoke the handler.
                mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

                if (asyncManager.isConcurrentHandlingStarted()) {
                    return;
                }

                applyDefaultViewName(processedRequest, mv);          //执行chain中拦截器附加的后处理方法,即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 other scenarios.
                dispatchException = new NestedServletException("Handler dispatch failed", err);
            }        //处理分发结果:包括解析视图并进行视图渲染,执行chain中拦截器附加的后处理方法,即afterCompletion方法。具体方法内容见下方源码。
            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);
                }
            }
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81

把这段代码拆分一下,一步一步的来
第一步:

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
		**当前请求
    	HttpServletRequest processedRequest = request;
    	*HandlerMapping会把请求映射为HandlerExecutionChain类型的handler对象;
		处理器执行链=我们即将执行的controller+拦截器
    	HandlerExecutionChain mappedHandler = null;
    	*判断是否是文件上传请求
    	*(MVC默认不支持文件格式)
    	boolean multipartRequestParsed = false;
    	*主要用来管理异步请求;什么时候用到异步请求呢?
    	**业务逻辑复杂的时候,为了防止请求线程阻塞,需要委托给另一个线程的时候
    	WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

扩展(什么是拦截器?)
Spring MVC中的拦截器(Interceptor)类似于Servlet中的过滤器(Filter),它主要用于拦截用户请求并作相应的处理。例如通过拦截器可以进行权限验证、记录请求信息的日志、判断用户是否登录等。
要使用Spring MVC中的拦截器,就需要对拦截器类进行定义和配置。通常拦截器类可以通过两种方式来定义:
1.通过实现HandlerInterceptor接口,或继承HandlerInterceptor接口的实现类(如HandlerInterceptorAdapter)来定义。
2.通过实现WebRequestInterceptor接口,或继承WebRequestInterceptor接口的实现类来定义。

第二步:当收到请求时 checkMultipart() 方法开始执行,具体看看这个方法

1.首先说一下这个方法的作用:判断请求中是否包含文件,那么,如何判断呢?接下来一起看看这个方法具体做了啥
  protected HttpServletRequest checkMultipart(HttpServletRequest request) throws MultipartException {
  *首先两个判断:判断这个文件是否为空;判断请求中是否包含文件*
  *.isMultipart(request):这个方法就是判断请求中是否包含文件*
        if (this.multipartResolver != null && this.multipartResolver.isMultipart(request)) {
        *其次再获取到的request是否为空*
        *WebUtils:spring的工具类,主要用于Web应用程序,供各种框架使用。*
         *getNativeRequest():抽象了各种request,如果要获得实际真正的request,则调getNativeRequest()或者getNativeRequest(XXX.class)方法*
            if (WebUtils.getNativeRequest(request, MultipartHttpServletRequest.class) != null) {
             	*打印日志*
                this.logger.debug("Request is already a MultipartHttpServletRequest - if not in a forward, this typically results from an additional MultipartFilter in web.xml");
            } else {
            	*判断request的一场是否和MultipartException相同,不是这个异常,正常返回解析数据,是这个异常就表示请中包含文件,打印日志,就直接把这个请求返回去了*
                if (!(request.getAttribute("javax.servlet.error.exception") instanceof MultipartException)) {
               		*resolveMultipart:对请求的数据进行解析,解析成MultipartFile*
                 	*因为这个方法的返回值是HttpServletRequest,所以解析成 MultipartFile并封装在 MultipartHttpServletRequest (继承了 HttpServletRequest) 对象中,返回*
                    return this.multipartResolver.resolveMultipart(request);
                }
                this.logger.debug("Multipart resolution failed for current request before - skipping re-resolution for undisturbed error rendering");
            }
        }
        return request;
    }
    
流程分析
MultipartResolver 用于处理文件上传,当收到请求时 DispatcherServlet 的 checkMultipart() 方法会调用 MultipartResolver 的 isMultipart() 方法判断请求中是否包含文件。如果请求数据中包含文件,则调用 MultipartResolver 的 resolveMultipart() 方法对请求的数据进行解析,然后将文件数据解析成 MultipartFile 并封装在 MultipartHttpServletRequest (继承了 HttpServletRequest) 对象中,最后传递给 Controller
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

3.1扩展(关于 this.logger.trace,this.logger.debug)
log4j定义了8个级别的log(除去OFF和ALL,可以说分为6个级别),优先级从高到低依次为:OFF、FATAL、ERROR、WARN、INFO、DEBUG、TRACE、 ALL。
1.ALL 最低等级的,用于打开所有日志记录。
2.TRACE designates finer-grained informational events than the DEBUG.Since:1.2.12,很低的日志级别,一般不会使用。
3.DEBUG 指出细粒度信息事件对调试应用程序是非常有帮助的,主要用于开发过程中打印一些运行信息。
4.INFO 消息在粗粒度级别上突出强调应用程序的运行过程。打印一些你感兴趣的或者重要的信息,这个可以用于生产环境中输出程序运行的一些重要信息,但是不能滥用,避免打印过多的日志。
5.WARN 表明会出现潜在错误的情形,有些信息不是错误信息,但是也要给程序员的一些提示。
6.ERROR 指出虽然发生错误事件,但仍然不影响系统的继续运行。打印错误和异常信息,如果不想输出太多的日志,可以使用这个级别。
7.FATAL 指出每个严重的错误事件将会导致应用程序的退出。这个级别比较高了。重大错误,这种级别你可以直接停止程序了。
8.OFF 最高等级的,用于关闭所有日志记录。
如果将log level设置在某一个级别上,那么比此级别优先级高的log都能打印出来。例如,如果设置优先级为WARN,那么OFF、FATAL、ERROR、WARN 4个级别的log能正常输出,而INFO、DEBUG、TRACE、 ALL级别的log则会被忽略。Log4j建议只使用四个级别,优先级从高到低分别是ERROR、WARN、INFO、DEBUG。

第三步:根据请求路径寻找controller,具体怎么找呢?看接下来的getHandler(processedRequest)方法

> checkMultipart()检查完这个请求合格后把请求路径返回到DispatcherServlet
DispatcherServlet(分发作用),所以它知道路径检查完合格后,应该给HandlerMapping
接下来的方法就是DispatcherServlet把路径给到HandlerMapping后的操作
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
        if (this.handlerMappings != null) {
        	*迭代这个handlerMappings
            Iterator var2 = this.handlerMappings.iterator();
            while(var2.hasNext()) {
            HandlerMapping mapping = (HandlerMapping)var2.next();
            HandlerExecutionChain handler =mapping.getHandler(request);
                if (handler != null) {
                    return handler;
                }
            }
        }
        return null;
    }
这段代码的核心就是getHandler(request)这个方法,我们一起看看
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
	//获取handler的具体逻辑,留给子类实现
	Object handler = getHandlerInternal(request);
	//null handler,采用默认的handler,即属性defaultHandler
	if (handler == null) {
		handler = getDefaultHandler();
	}
	//还是null handler,直接返回null
	if (handler == null) {
		return null;
	}
	//handler是beanName, 从容器里获取对应的bean
	// Bean name or resolved handler?
	if (handler instanceof String) {
		String handlerName = (String) handler;
		handler = obtainApplicationContext().getBean(handlerName);
	}

	//根据handler和request获取处理器链HandlerExecutionChain
	HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
	//CORS请求的处理
	if (CorsUtils.isCorsRequest(request)) {
		//全局配置
		CorsConfiguration globalConfig = this.globalCorsConfigSource.getCorsConfiguration(request);
		//handler的单独配置
		CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
		//handler的所有配置,全局配置+单独配置
		CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
		//根据cors配置更新HandlerExecutionChain
		executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
	}
	return executionChain;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51

logger.isTraceEnabled方法作用是判断记录器Trace跟踪是否激活。Trace跟踪激活后一般会打印比较详细的信息。
第四步:上一步可以说是获取到conreoller 的名字了,然后根据controller的名字找对应的controller,
具体看这个方法:
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
		for (HandlerAdapter ha : this.handlerAdapters) {
			*logger.isTraceEnabled方法作用是判断记录器Trace跟踪是否激活。Trace跟踪激活后一般会打印比较详细的信息。*
			if (logger.isTraceEnabled()) {
				logger.trace("Testing handler adapter [" + ha + "]");
			}
			*supports方法的主要作用在于判断当前的HandlerAdapter是否能够支持当前的handler的适配*
			if (ha.supports(handler)) {
			如果支持就返回这个HandlerAdapter(适配器)
				return ha;
			}
		}
		throw new ServletException("No adapter for handler [" + handler +"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
	}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

第五步:然后再来分析接下来这一段

getLastModified()关于这个方法我给大家找了一篇博客,大家可以看看

	// Process last-modified header, if supported by the handler.
				String method = request.getMethod();
				boolean isGet = "GET".equals(method);
				if (isGet || "HEAD".equals(method)) {
				ha表示一个适配器,适配器调用了一下这个getLastModified()方法,传了两个参数一个是mappedHandler.getHandler()方法返回来的handler(controller),一个是请求
					long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
					然后就是打印日志
					if (logger.isDebugEnabled()) {
						logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
					}
					判断getLastModified的返回值是不是文件,以及是不是get方法,不是文件,是Get方法的话就返回去
					if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
						return;
					}
				}
				applyPreHandle()方法在获取到所有拦截器后,通过for循环调用其中每个拦截器HandlerInterceptor的preHandle()方法,此过程可以类比对用户数据进行加密的过程
				if (!mappedHandler.applyPreHandle(processedRequest, response)) {
					return;
				}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

剩下的都在刚开始那段代码中了,其实这个也没啥就是简单的看看MVC工作的时候底层在干啥,不合适的地方多多指教

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Monodyee/article/detail/67731
推荐阅读
相关标签
  

闽ICP备14008679号