SpringMvc---SpringMvc的执行过程
1、SpringMVC常用组件
-
DispatcherServlet:前端控制器,不需要工程师开发,由框架提供
作用:统一处理请求和响应,整个流程控制的中心,由它调用其它组件处理用户的请求
-
HandlerMapping:处理器映射器,不需要工程师开发,由框架提供
作用:根据请求的url、method等信息查找Handler,即将请求和控制器方法建立映射关系
-
Handler:处理器,需要工程师开发
作用:在DispatcherServlet的控制下Handler对具体的用户请求进行处理 控制器方法
-
HandlerAdapter:处理器适配器,不需要工程师开发,由框架提供
作用:通过HandlerAdapter对处理器(控制器方法)进行执行,调用控制器方法
-
ViewResolver:视图解析器,不需要工程师开发,由框架提供
作用:进行视图解析,得到相应的视图,例如:ThymeleafView、InternalResourceView、RedirectView
-
View:视图
作用:将模型数据通过页面展示给用户
2、SpringMvc的初始化
DispatcherServlet本质也是一个Servlet,他的生命周期也遵循servlet的规则,初始化也应该从init方法开始。
首先进入DiapatcherServlet中,再进入他的父类FrameworkServlet,再进入FrameworkServlet的父类HttpServletBean、在进入HttpServletBean的父类HttpServlet、再进入HttpServlet的父类GenericServlet、最后发现GenericServlet实现了Servlet的接口。已知在Servlet接口中有一个初始化方法init
使用IDEA左下角工具栏的structure工具可以看见当前类中的方法和继承关系,方法后出现向上的箭头就是来自父类的方法
接下来分别在GenericServlet、HttpServlet、HttpServletBean、FrameworkServlet、DiapatcherServlet中找实现了父类的init方法。发现在GenericServlet中init的工作是把ServletConfig的值赋给成员变量,在HttpServlet中没有实现init方法,在HttpServletBean中init方法调用了一个initServletBean()方法,在initServletBean()方法中什么都没写。在FrameworkServlet中对它父类的initServletBean()进行了编写内容以及再次调用了一个方法initWebApplicationContext(),在initWebApplicationContext()方法中
首先通过工具类的getWebApplicationContext()方法创建了一个WebApplicationContext类型的对象,又在底下再次创建了一个对象。对第二次创建的对象进行判断是否为空。由于是从初始化一路找来的,并没有对这个对象进行任何操作,他肯定为空。在下面的判断中,如果为null,首先会去找,没找到就会创建一个WebApplicationContext对象。在创建这个WebApplicationContext对象的方法里可以看见创建对象后分别调用
wac.setEnvironment(this.getEnvironment()); wac.setParent(parent);
方法给该对象设置环境和设置父容器。在SSM整合时,整合后会有两个配置文件,一个属于Sping一个属于SpringMvc,也就会有两个IOC容器,这时父容器是Spring子容器是SpringMvc的IOC容器。由于SpringMvc和Spring不整合也能直接用,这时就只会有一个IOC容器。就没有父子容器之分
最后将新创建的的WebApplicationContext对象返回。
再次返回initWebApplicationContext方法,在创建完成后。就会执行刷新方法。执行完成后就会再执行最后一个if判断,获取当前的ServletContextAttributeName,再用getServletContext().setAttribute(attrName, wac);将ServletContextAttributeName和创建的WebApplicationContext对象建立映射关系。在整个应用层共享。
整个初始化就完了。
在刷新步骤,执行的是父类的onRefresh的方法,但是实际上这个方法是在子类中进行填充的。在子类dispatcherServlet中的onRefresh方法里调用了initStrategies方法【初始化策略】,在这个方法里规定了很多初始化要做的事和要执行的方法。
所以要让DispatcherServlet类在服务器开启的时候就初始化
rotected WebApplicationContext initWebApplicationContext() { WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(this.getServletContext()); WebApplicationContext wac = null; if (this.webApplicationContext != null) { wac = this.webApplicationContext; if (wac instanceof ConfigurableWebApplicationContext) { ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext)wac; if (!cwac.isActive()) { if (cwac.getParent() == null) { cwac.setParent(rootContext); } this.configureAndRefreshWebApplicationContext(cwac); } } } if (wac == null) { wac = this.findWebApplicationContext(); } if (wac == null) { wac = this.createWebApplicationContext(rootContext); } if (!this.refreshEventReceived) { synchronized(this.onRefreshMonitor) { this.onRefresh(wac); } } if (this.publishContext) { String attrName = this.getServletContextAttributeName(); this.getServletContext().setAttribute(attrName, wac); } return wac; }
3、DispatcherServlet调用组件处理请求过程
这个就只要在DispatcherServlet中就能看见调用了什么组件进行实现过程。
前面在初始化的时候刷新时调用了一个方法initStrategies
protected void initStrategies(ApplicationContext context) { this.initMultipartResolver(context); this.initLocaleResolver(context); this.initThemeResolver(context); this.initHandlerMappings(context); this.initHandlerAdapters(context); this.initHandlerExceptionResolvers(context); this.initRequestToViewNameTranslator(context); this.initViewResolvers(context); this.initFlashMapManager(context); }
在这个方法里,目前看的懂的,
规定了建立请求和控制器方法之间的映射关系 this.initHandlerMappings(context);
规定了调用控制器方法的 this.initHandlerAdapters(context);
加载文件上传的MultipartResolver类this.initMultipartResolver(context);
加载异常处理器this.initHandlerExceptionResolvers(context);
加载视图控制器this.initViewResolvers(context);
刚才是通过Servlet的生命周期中的init方法贯穿整个DispatcherServlet的初始化,现在也可以通过service方法找出整个过程中会调用的service方法和相关方法
service顺序
首先在servlet接口中存在service方法、
在GenericServlet中实现了这个service方法、
在HttpServlet中的service中对处理各类请求调用不同方法做了明确规定,在这个类中创建了不同的 do请求形式方法,对不同请求进行处理。
在HttpServletBean中没有service方法,
在framework中则调用了HttpServlet的所有do方法,发现 好几个do方法里面调用了一个processRequest方法。点进去这个方法发现它里面调用了一个抽象方法 abstract void doService。
再次进入DispatcherServlet类中发现它重写了父类的doService方法。在这个 doService方法中找到了它调用了doDispatcher方法。
doDispatcher方法中
然后再到DispatcherServlet中观察,发现首先是剩下执行调用组件的方法是doDispatcher()。
在dodispatcher方法中来到熟悉的 mv=ha.handle....,在上面是调用的拦截器的applypreHandle方法,下面是执行的applyPostHandle方法,
打上断点,观察执行顺序。
执行顺序:
首先来到doService中调用doDispatcher方法那一行,点击下一行,
然后直接进入doDispatcher方法中调用applyPrehandle方法这一行,此时发现执行的是一个HandlerExecutionChain with [xlw.com.last.controller.TestController#test1()] and 3 interceptors,里面有控制器方法和Interceptor链【里面有SpringMvc自带的和自己定义的interceptor】。进入applyPreHandle方法里面进入for循环顺序【++】执行拦截器链中的拦截器方法。【测试拦截器就先返回true】
之后执行完后,跳回doDispatcher中的 mv=ha.handle....这一行,获取视图名。再跳到下一个断点,这个断点应该在控制器方法中,来到控制器方法执行完控制器方法返回视图名。
接下来跳到调用applyPostHandle方法的这一行,点击下一行进入方法内部,进入for循环逆序【--】执行,执行拦截器链的拦截器方法。
执行完了后跳到调用processDispatchResult方法这一行,进入这个方法里面首先是对控制器方法执行结果的检查是否有异常并执行后续工作,没有异常就会调用render方法进行渲染视图。
最后调用triggerAfterCompletion方法执行拦截器链的AfterCompletion,点击下一行,进入方法内部,在for循环中逆序执行拦截器链中的afterCompletion方法执行完成后。
断点就完了,在浏览器页面返回处理的结果和渲染后的视图。
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { HttpServletRequest processedRequest = request; HandlerExecutionChain mappedHandler = null; boolean multipartRequestParsed = false; WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); try { try { ModelAndView mv = null; Object dispatchException = null; try { processedRequest = this.checkMultipart(request); multipartRequestParsed = processedRequest != request; mappedHandler = this.getHandler(processedRequest); if (mappedHandler == null) { this.noHandlerFound(processedRequest, response); return; } HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler()); 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()); if (asyncManager.isConcurrentHandlingStarted()) { return; } this.applyDefaultViewName(processedRequest, mv); mappedHandler.applyPostHandle(processedRequest, response, mv); } catch (Exception var20) { dispatchException = var20; } catch (Throwable var21) { dispatchException = new NestedServletException("Handler dispatch failed", var21); } 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); } } }
4、SpringMvc的执行流程
首先,浏览器向服务器发送请求,这时由DispatcherServlet接收请求,将请求解析为请求资源标识符URI。拿到请求后在配置文件里面通过组件扫描,找到控制器类并根据URI找对应的映射。没找到就报404,如果开放了静态资源的访问【default-servlet-handle】,再到默认的servlet里面去找有没有对应的映射。没有就报404
然后,如果找到对应的映射那就执行映射对应的控制器方法,
这个过程里面,根据URI调用HandlerMapping【处理器映射器】获取这个handler对应的对象和方法,这些对象包括handler对象和拦截器。形成一个 HandlerExecutionChain【就是刚才在doDispatcher方法中执行的那个链】里面有控制器类、控制器方法、拦截器集合【链】 ,进入doDispatcher,根据获取到的Handle找到一个HandleAdapter【处理器适配器】
然后调用拦截器的applyPreHandle方法,正序执行拦截器链中的拦截器的方法。
再执行mv=ha.handle....这一行,提取请求中的模型数据,填充到Handle入参,
进入控制器方法,在控制器方法中执行存取数据、获取请求响应报文 、数据转换、数据验证、数据格式化等操作【这些操作就是控制器方法里面能做的操作】。之后返回视图名
在控制器方法执行完后返回【一个ModelAndView对象】后,执行拦截器的applyPostHandle方法【逆向执行 -- 】
执行完后就对返回的ModelAndView对象判断有没有异常,有异常就用配置的异常处理器,得到一个错误处理页面返回到浏览器上显示。
没有异常就根据配置的视图解析器【View-resolver】对视图进行解析并渲染。
渲染完毕后会执行拦截器afterCompletion方法【逆序执行 --】
最后把渲染的结果返回给浏览器

浙公网安备 33010602011771号