今天分析的是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>
然后按住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); } } } }
把这段代码拆分一下,一步一步的来
第一步:
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);
扩展(什么是拦截器?)
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
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; }
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"); }
第五步:然后再来分析接下来这一段
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; }
剩下的都在刚开始那段代码中了,其实这个也没啥就是简单的看看MVC工作的时候底层在干啥,不合适的地方多多指教。