课程咨询 :15626927836
咨询QQ:2162606306

  • SpringMVC执行流程及源码解析

    发布:珠海android培训      来源:iteye      时间:2017-05-18

  • 官方文档中的流程
    首先看下SpringMVC文档上给的流程图:



    这张图片给了我们大概的执行流程:
    • 用户请求首先发送到前端控制器DispatcherServlet,DispatcherServlet根据请求的信息来决定使用哪个页面控制器Controller(也就是我们通常编写的Controller)来处理该请求。找到控制器之后,DispatcherServlet将请求委托给控制器去处理。
    • 接下来页面控制器开始处理用户请求,页面控制器会根据请求信息进行处理,调用业务层等等,处理完成之后,会把结果封装成一个ModelAndView返回给DispatcherServlet。
    • 前端控制器DispatcherServlet接到页面控制器的返回结果后,根据返回的视图名选择相应的试图模板,并根据返回的数据进行渲染。
    • 最后前端控制器DispatcherServlet将结果返回给用户。
    更具体的流程
    上面只是总体流程,接下来我们稍微深入一点,看下更具体的流程,这里没有图,只有步骤解析:
    • 用户请求发送到前端控制器DispatcherServlet。
    • 前端控制器DispatcherServlet接收到请求后,DispatcherServlet会使用HandlerMapping来处理,HandlerMapping会查找到具体进行处理请求的Handler对象。
    • HandlerMapping找到对应的Handler之后,并不是返回一个Handler原始对象,而是一个Handler执行链,在这个执行链中包括了拦截器和处理请求的Handler。HandlerMapping返回一个执行链给DispatcherServlet。
    • DispatcherServlet接收到执行链之后,会调用Handler适配器去执行Handler。
    • Handler适配器执行完成Handler(也就是我们写的Controller)之后会得到一个ModelAndView,并返回给DispatcherServlet。
    • DispatcherServlet接收到Handler适配器返回的ModelAndView之后,会根据其中的视图名调用视图解析器。
    • 视图解析器根据逻辑视图名解析成一个真正的View视图,并返回给DispatcherServlet。
    • DispatcherServlet接收到视图之后,会根据上面的ModelAndView中的model来进行视图渲染完成之后,DispatcherServlet就可以将结果返回给用户了。

    源码
    DispatcherServlet是一个Servlet,我们知道在Servlet在处理一个请求的时候会交给service方法进行处理,这里也不例外,DispatcherServlet继承了FrameworkServlet,首先进入FrameworkServlet的service方法:
    Java代码
    1. protected void service(HttpServletRequest request, HttpServletResponse response)  
    2.         throws ServletException, IOException {  
    3.     //请求方法  
    4.     String method = request.getMethod();  
    5.     //PATCH方法单独处理  
    6.     if (method.equalsIgnoreCase(RequestMethod.PATCH.name())) {  
    7.         processRequest(request, response);  
    8.     }  
    9.     else {//其他的请求类型的方法经由父类,也就是HttpServlet处理  
    10.         super.service(request, response);  
    11.     }  
    12. }  

    HttpServlet中会根据请求类型的不同分别调用doGet或者doPost等方法,FrameworkServlet中已经重写了这些方法,在这些方法中会调用processRequest进行处理,在processRequest中会调用doService方法,这个doService方法就是在DispatcherServlet中实现的。下面就看下DispatcherServlet中的doService方法的实现。

    请求到达DispatcherServlet
    doService方法:
    Java代码
    1. protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {  
    2.   
    3.     //给request中的属性做一份快照  
    4.     Map<String, Object> attributesSnapshot = null;  
    5.     if (WebUtils.isIncludeRequest(request)) {  
    6.         logger.debug("Taking snapshot of request attributes before include");  
    7.         attributesSnapshot = new HashMap<String, Object>();  
    8.         Enumeration<?> attrNames = request.getAttributeNames();  
    9.         while (attrNames.hasMoreElements()) {  
    10.             String attrName = (String) attrNames.nextElement();  
    11.             if (this.cleanupAfterInclude || attrName.startsWith("org.springframework.web.servlet")) {  
    12.                 attributesSnapshot.put(attrName, request.getAttribute(attrName));  
    13.             }  
    14.         }  
    15.     }  
    16.   
    17.     //如果我们没有配置类似本地化或者主题的处理器之类的  
    18.     //SpringMVC会使用默认的值  
    19.     //默认配置文件是DispatcherServlet.properties  
    20.     request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());  
    21.     request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);  
    22.     request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);  
    23.     request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());  
    24.   
    25.     FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);  
    26.     if (inputFlashMap != null) {  
    27.         request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));  
    28.     }  
    29.     request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());  
    30.     request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);  
    31.   
    32.     try {  
    33.         //开始处理  
    34.         doDispatch(request, response);  
    35.     }  
    36.     finally {  
    37.         if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {  
    38.             return;  
    39.         }  
    40.         // Restore the original attribute snapshot, in case of an include.  
    41.         if (attributesSnapshot != null) {  
    42.             restoreAttributesAfterInclude(request, attributesSnapshot);  
    43.         }  
    44.     }  
    45. }  

    DispatcherServlet开始真正的处理,doDispatch方法
    Java代码
    1. protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {  
    2.     HttpServletRequest processedRequest = request;  
    3.     HandlerExecutionChain mappedHandler = null;  
    4.     boolean multipartRequestParsed = false;  
    5.     //SpringMVC中异步请求的相关知识,暂先不解释  
    6.     WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);  
    7.   
    8.     try {  
    9.         ModelAndView mv = null;  
    10.         Exception dispatchException = null;  
    11.   
    12.         try {  
    13.             //先检查是不是Multipart类型的,比如上传等  
    14.             //如果是Multipart类型的,则转换为MultipartHttpServletRequest类型  
    15.             processedRequest = checkMultipart(request);  
    16.             multipartRequestParsed = processedRequest != request;  
    17.   
    18.             //获取当前请求的Handler  
    19.             mappedHandler = getHandler(processedRequest, false);  
    20.             if (mappedHandler == null || mappedHandler.getHandler() == null) {  
    21.                 noHandlerFound(processedRequest, response);  
    22.                 return;  
    23.             }  
    24.   
    25.             //获取当前请求的Handler适配器  
    26.             HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());  
    27.   
    28.             // 对于header中last-modified的处理  
    29.             String method = request.getMethod();  
    30.             boolean isGet = "GET".equals(method);  
    31.             if (isGet || "HEAD".equals(method)) {  
    32.                 long lastModified = ha.getLastModified(request, mappedHandler.getHandler());  
    33.                 if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {  
    34.                     return;  
    35.                 }  
    36.             }  
    37.             //拦截器的preHandle方法进行处理  
    38.             if (!mappedHandler.applyPreHandle(processedRequest, response)) {  
    39.                 return;  
    40.             }  
    41.   
    42.             try {  
    43.                 //真正调用Handler的地方  
    44.                 mv = ha.handle(processedRequest, response, mappedHandler.getHandler());  
    45.             }  
    46.             finally {  
    47.                 if (asyncManager.isConcurrentHandlingStarted()) {  
    48.                     return;  
    49.                 }  
    50.             }  
    51.             //处理成默认视图名,就是添加前缀和后缀等  
    52.             applyDefaultViewName(request, mv);  
    53.             //拦截器postHandle方法进行处理  
    54.             mappedHandler.applyPostHandle(processedRequest, response, mv);  
    55.         }  
    56.         catch (Exception ex) {  
    57.             dispatchException = ex;  
    58.         }  
    59.         //处理最后的结果,渲染之类的都在这里  
    60.         processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);  
    61.     }  
    62.     catch (Exception ex) {  
    63.         triggerAfterCompletion(processedRequest, response, mappedHandler, ex);  
    64.     }  
    65.     catch (Error err) {  
    66.         triggerAfterCompletionWithError(processedRequest, response, mappedHandler, err);  
    67.     }  
    68.     finally {  
    69.         if (asyncManager.isConcurrentHandlingStarted()) {  
    70.             // Instead of postHandle and afterCompletion  
    71.             mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);  
    72.             return;  
    73.         }  
    74.         // Clean up any resources used by a multipart request.  
    75.         if (multipartRequestParsed) {  
    76.             cleanupMultipart(processedRequest);  
    77.         }  
    78.     }  
    79. }  

    可以看到大概的步骤还是按照我们上面分析的走的。

    查找请求对应的Handler对象
    对应着这句代码mappedHandler = getHandler(processedRequest, false);,看下具体的getHandler方法:
    Java代码
    1. protected HandlerExecutionChain getHandler(HttpServletRequest request, boolean cache) throws Exception {  
    2.     return getHandler(request);  
    3. }  

    继续往下看getHandler:
    Java代码
    1. protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {  
    2.     //遍历所有的handlerMappings进行处理  
    3.     //handlerMappings是在启动的时候预先注册好的  
    4.     for (HandlerMapping hm : this.handlerMappings) {  
    5.         HandlerExecutionChain handler = hm.getHandler(request);  
    6.         if (handler != null) {  
    7.             return handler;  
    8.         }  
    9.     }  
    10.     return null;  
    11. }  

    继续往下看getHandler,在AbstractHandlerMapping类中:
    Java代码
    1. public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {  
    2.     //根据request获取handler  
    3.     Object handler = getHandlerInternal(request);  
    4.     if (handler == null) {  
    5.         //如果没有找到就使用默认的handler  
    6.         handler = getDefaultHandler();  
    7.     }  
    8.     if (handler == null) {  
    9.         return null;  
    10.     }  
    11.     //如果Handler是String,表明是一个bean名称  
    12.     //需要超照对应bean  
    13.     if (handler instanceof String) {  
    14.         String handlerName = (String) handler;  
    15.         handler = getApplicationContext().getBean(handlerName);  
    16.     }  
    17.     //封装Handler执行链  
    18.     return getHandlerExecutionChain(handler, request);  
    19. }  

    根据requrst获取handler
    首先看下根据requrst获取handler步骤getHandlerInternal方法,在AbstractHandlerMethodMapping中:
    Java代码
    1. protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {  
    2.     //获取request中的url,用来匹配handler  
    3.     String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);  
    4.     //根据路径寻找Handler  
    5.     HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);  
    6.     //根据handlerMethod中的bean来实例化Handler并添加进HandlerMethod  
    7.     return (handlerMethod != null) ? handlerMethod.createWithResolvedBean() : null;  
    8. }  

    看下根据路径寻找handler的方法lookupHandlerMethod:
    Java代码
    1. protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {  
    2.     List<Match> matches = new ArrayList<Match>();  
    3.     //直接匹配  
    4.     List<T> directPathMatches = this.urlMap.get(lookupPath);  
    5.     //如果有匹配的,就添加进匹配列表中  
    6.     if (directPathMatches != null) {  
    7.         addMatchingMappings(directPathMatches, matches, request);  
    8.     }  
    9.     //还没有匹配的,就遍历所有的处理方法查找  
    10.     if (matches.isEmpty()) {  
    11.         // No choice but to go through all mappings  
    12.         addMatchingMappings(this.handlerMethods.keySet(), matches, request);  
    13.     }  
    14.     //找到了匹配的  
    15.     if (!matches.isEmpty()) {  
    16.         Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));  
    17.         Collections.sort(matches, comparator);  
    18.         //排序之后,获取第一个  
    19.         Match bestMatch = matches.get(0);  
    20.         //如果有多个匹配的,会找到第二个最合适的进行比较一下  
    21.         if (matches.size() > 1) {  
    22.             Match secondBestMatch = matches.get(1);  
    23.             if (comparator.compare(bestMatch, secondBestMatch) == 0) {  
    24.                 Method m1 = bestMatch.handlerMethod.getMethod();  
    25.                 Method m2 = secondBestMatch.handlerMethod.getMethod();  
    26.                 throw new IllegalStateException(  
    27.                         "Ambiguous handler methods mapped for HTTP path '" + request.getRequestURL() + "': {" +  
    28.                         m1 + ", " + m2 + "}");  
    29.             }  
    30.         }  
    31.         //设置request参数  
    32.         handleMatch(bestMatch.mapping, lookupPath, request);  
    33.         //返回匹配的url的处理的方法  
    34.         return bestMatch.handlerMethod;  
    35.     }  
    36.     else {//最后还没有找到,返回null  
    37.         return handleNoMatch(handlerMethods.keySet(), lookupPath, request);  
    38.     }  
    39. }  

    获取默认Handler
    如果上面没有获取到Handler,就会获取默认的Handler。如果还获取不到就返回null。

    处理String类型的Handler
    如果上面处理完的Handler是String类型的,就会根据这个handlerName获取bean。

    封装Handler执行链
    上面获取完Handler,就开始封装执行链了,就是将我们配置的拦截器加入到执行链中去,getHandlerExecutionChain:
    Java代码
    1. protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {  
    2.     //如果当前Handler不是执行链类型,就使用一个新的执行链实例封装起来  
    3.     HandlerExecutionChain chain =  
    4.         (handler instanceof HandlerExecutionChain) ?  
    5.             (HandlerExecutionChain) handler : new HandlerExecutionChain(handler);  
    6.     //先获取适配类型的拦截器添加进去拦截器链  
    7.     chain.addInterceptors(getAdaptedInterceptors());  
    8.     //当前的url  
    9.     String lookupPath = urlPathHelper.getLookupPathForRequest(request);  
    10.     //遍历拦截器,找到跟当前url对应的,添加进执行链中去  
    11.     for (MappedInterceptor mappedInterceptor : mappedInterceptors) {  
    12.         if (mappedInterceptor.matches(lookupPath, pathMatcher)) {  
    13.             chain.addInterceptor(mappedInterceptor.getInterceptor());  
    14.         }  
    15.     }  
    16.   
    17.     return chain;  
    18. }  

    获取对应请求的Handler适配器
    getHandlerAdapter:
    Java代码
    1. protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {  
    2.     //遍历所有的HandlerAdapter,找到和当前Handler匹配的就返回  
    3.     //我们这里会匹配到RequestMappingHandlerAdapter  
    4.     for (HandlerAdapter ha : this.handlerAdapters) {  
    5.         if (ha.supports(handler)) {  
    6.             return ha;  
    7.         }  
    8.     }  
    9. }  

    缓存的处理
    也就是对last-modified的处理

    执行拦截器的preHandle方法
    就是遍历所有的我们定义的interceptor,执行preHandle方法

    使用Handler适配器执行当前的Handler
    ha.handle执行当前Handler,我们这里使用的是RequestMappingHandlerAdapter,首先会进入AbstractHandlerMethodAdapter的handle方法:
    Java代码
    1. public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)  
    2.         throws Exception {  
    3.     return handleInternal(request, response, (HandlerMethod) handler);  
    4. }  

    handleInternal方法,在RequestMappingHandlerAdapter中:
    Java代码
    1. protected final ModelAndView handleInternal(HttpServletRequest request,  
    2.         HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {  
    3.   
    4.     if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {  
    5.         // Always prevent caching in case of session attribute management.  
    6.         checkAndPrepare(request, response, this.cacheSecondsForSessionAttributeHandlers, true);  
    7.     }  
    8.     else {  
    9.         // Uses configured default cacheSeconds setting.  
    10.         checkAndPrepare(request, response, true);  
    11.     }  
    12.   
    13.     // Execute invokeHandlerMethod in synchronized block if required.  
    14.     if (this.synchronizeOnSession) {  
    15.         HttpSession session = request.getSession(false);  
    16.         if (session != null) {  
    17.             Object mutex = WebUtils.getSessionMutex(session);  
    18.             synchronized (mutex) {  
    19.                 return invokeHandleMethod(request, response, handlerMethod);  
    20.             }  
    21.         }  
    22.     }  
    23.     //执行方法,封装ModelAndView  
    24.     return invokeHandleMethod(request, response, handlerMethod);  
    25. }  

    组装默认视图名称
    前缀和后缀名都加上

    执行拦截器的postHandle方法
    遍历intercepter的postHandle方法。

    处理最后的结果,渲染之类的
    processDispatchResult方法:
    Java代码
    1. private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,  
    2.         HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception {  
    3.   
    4.     boolean errorView = false;  
    5.   
    6.     if (exception != null) {  
    7.         if (exception instanceof ModelAndViewDefiningException) {  
    8.             mv = ((ModelAndViewDefiningException) exception).getModelAndView();  
    9.         }  
    10.         else {  
    11.             Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);  
    12.             mv = processHandlerException(request, response, handler, exception);  
    13.             errorView = (mv != null);  
    14.         }  
    15.     }  
    16.   
    17.     // Did the handler return a view to render?  
    18.     if (mv != null && !mv.wasCleared()) {  
    19.         //渲染  
    20.         render(mv, request, response);  
    21.         if (errorView) {  
    22.             WebUtils.clearErrorRequestAttributes(request);  
    23.         }  
    24.     }  
    25.     else {  
    26.     }  
    27.   
    28.     if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {  
    29.         // Concurrent handling started during a forward  
    30.         return;  
    31.     }  
    32.   
    33.     if (mappedHandler != null) {  
    34.         mappedHandler.triggerAfterCompletion(request, response, null);  
    35.     }  
    36. }  

    重点看下render方法,进行渲染:
    Java代码
    1. protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {  
    2.     //设置本地化  
    3.     Locale locale = this.localeResolver.resolveLocale(request);  
    4.     response.setLocale(locale);  
    5.   
    6.     View view;  
    7.     if (mv.isReference()) {  
    8.         //解析视图名,得到视图  
    9.         view = resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request);  
    10.     }  
    11.     else {  
    12.         // No need to lookup: the ModelAndView object contains the actual View object.  
    13.         view = mv.getView();  
    14.         if (view == null) {  
    15.             throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a " +  
    16.                     "View object in servlet with name '" + getServletName() + "'");  
    17.         }  
    18.     }  
    19.   
    20.     //委托给视图进行渲染  
    21.     view.render(mv.getModelInternal(), request, response);  
    22. }  
    23.   
    24. view.render就是进行视图的渲染,然后跳转页面等处理。

上一篇: 2017年Java调查报告

下一篇:中国互联网的第二次世界大战

最新开班日期  |  更多

Android--高端全日制班

Android--高端全日制班

开班日期:9月28日

Android--零基础全日制班

Android--零基础全日制班

开班日期:9月28日

Android--高端周末班

Android--高端周末班

开班日期:9月28日

Android--零基础周末班

Android--零基础周末班

开班日期:9月28日

  • 地址:珠海市香洲区翠微金嘉创意谷
  • 课程培训电话:15626927836
    咨询QQ:2162606306     全国服务监督电话:400-111-8989
  • 服务邮箱 tousu@tedu.cn
  • 2001-2016 达内时代科技集团有限公司 版权所有 京ICP证8000853号-56