前言
当你知道springMVC的执行流程的时候,会达到是事半功倍的学习效果。
SpringMVC执行流程
-
首先明确:SpringMVC的执行过程就是:客户端或者浏览器发送请求到后端服务器,后端服务器会找到对应的方法执行相应的方法,这之间的复杂的过程就是SpringMVC执行流程的核心。
注意:这里的方法就是控制层(controller类中的的方法,同时这个方法在底层就是handler中的handlerMethod)。hander就是SpringMVC执行流程的主角之一。
-
客户端发送的请求到后端服务器,后端服务器首先会做什么呢?
我们都知道springMVC框架是为了搭建javaWeb项目使用的框架工具。所以说后端服务器接收到请求之后就和javaweb执行流程一样。如下图:
-
那有一个问题:SpringMVC框架体现在哪呢????
其实SpringMVC框架就体现在对以前的Servlet类进行包装,包装之后就成了SpringMVC核心代码了,这个代码就是DispatcherServlet类了
-
现在我们重点放在DispatcherServlet类中,因为SpringMVC的执行流程就在其中。
说到DispatcherServlet你应该要想到JavaWeb中的servlet类,并且知道Servlet类中对主要的方法:service()。
我们先来看DispatcherServlet的继承结构图。
主要看蓝色箭头:
Servlet(接口)→HttpServlet → HttpServletBean→FrameworkServlet→DispatcherServlet
假如前端发送了一个GET请求,在HttpServlet 中会判断请求方式,而HttpServletBean、FrameworkServlet这两个类做的是一些初始化工作,最终还是执行DispatcherServlet类中的方法。
-
我们都知道所有servlet中都有着一个最主要的方法:service()。在DispatcherServlet最主要的方法就是doservice()方法。
在这个方法中所干的事情先做个总结在看源代码,主要是做两件事:
1、设置参数,比如:设置应用上下文、字符编码、样式等等
2、调用处理请求的核心方法doDispatch(request, response);
@Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
if (logger.isDebugEnabled()) {
String resumed = WebAsyncUtils.getAsyncManager(request).hasConcurrentResult() ? " resumed" : "";
logger.debug("DispatcherServlet with name '" + getServletName() + "'" + resumed +
" processing " + request.getMethod() + " request for [" + getRequestUri(request) + "]");
}
Map<String, Object> attributesSnapshot = null;
if (WebUtils.isIncludeRequest(request)) {
attributesSnapshot = new HashMap<String, Object>();
Enumeration<?> attrNames = request.getAttributeNames();
while (attrNames.hasMoreElements()) {
String attrName = (String) attrNames.nextElement();
if (this.cleanupAfterInclude || attrName.startsWith("org.springframework.web.servlet")) {
attributesSnapshot.put(attrName, request.getAttribute(attrName));
}
}
}
request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());
FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
if (inputFlashMap != null) {
request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
}
request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);
try {
doDispatch(request, response);
}
finally {
if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
if (attributesSnapshot != null) {
restoreAttributesAfterInclude(request, attributesSnapshot);
}
}
}
}
- 接着我们再看处理请求的核心方法doDispatch(request, response)
通过request, response这两个参数,我们拿到了请求信息。这里主要做的事情有:
1、通过请求信息判断是否有文件上传
2、调用 getHandler(processedRequest);
这个方法是利用请求信息得到请求的地址,例如请求地址为:http://localhost:8080/target。
有了请求地址后再得到映射路径:/target 。(注意这个/target是通过请求路径得到的。)
说明:后端我们写的所有的controller方法的映射路径和方法都是以key-value的形式存储在HandlerMapping集合当中。例如:下图,key为/target,value为这个方法tar()
通过映射路径/target去遍历HandlerMapping,如果HandlerMapping中的key有/servlet,就返回对应的方法。如果没有就会返回404错误。
3、此时我们得到了方法tar(),接下来我们就是要去执行这个方法。
如何执行?
答:通过适配器执行,得先获得适配器:HandlerAdapter。
在源代码中执行这个方法tar()前后,是由拦截器的,作用就是进一步去看你这个方法合不合理。
合理的话就继续执行。过程如下:
拦截器前置方法执行:preHandler方法
执行方法,返回了ModelAndView
拦截器后置方法1执行:PostHandle方法
渲染视图
拦截器后置方法2执行: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 {
ModelAndView mv = null;
Exception dispatchException = null;
try {
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null || mappedHandler.getHandler() == null) {
noHandlerFound(processedRequest, response);
return;
}
HandlerAdapter ha = 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 (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;
}
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
applyDefaultViewName(processedRequest, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Error err) {
triggerAfterCompletionWithError(processedRequest, response, mappedHandler, err);
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}
else {
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}
到这里SpringMVC的执行流程就好像结束了。剩下的就是交给前端将渲染出来的视图展现给客户。
我这里的 视图渲染 没说太清楚。sorry
大总结:(面试的话可以按照下面的来说)
注意注意:(重要的事情说三遍)
如果有哪里不对的地方,欢迎在评论区告诉我!!!!!!!!!!!!!!!!!
如果有哪里不对的地方,欢迎在评论区告诉我!!!!!!!!!!!!!!!!!
如果有哪里不对的地方,欢迎在评论区告诉我!!!!!!!!!!!!!!!!!
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)