你的位置:首页 > Java教程

[Java教程]SpringMVC深入理解


核心类与接口

- DispatcherServlet 前置控制器
- HandlerMapping 请求映射(到Controller)
- HandlerAdapter 请求映射(到Controller类的方法上)
- Controller 控制器
- HandlerIntercepter 拦截器
- ViewResolver 视图映射
- View 视图处理

启动过程


Spring MVC启动过程大致分为两个过程:
- ContextLoaderListener初始化,读取context-param中的contextConfigLocation指定的配置文件,创建ROOT Context,通过调用继承自ContextLoader的initWebApplicationContext方法实例化Spring IoC容器,并将此容器实例注册到ServletContext中
- ContextLoaderListener初始化完毕后,开始初始化web.

复制代码
 1  /** 2   * Initialize the strategy objects that this servlet uses. 3   * <p>May be overridden in subclasses in order to initialize further strategy objects. 4   */ 5   protected void initStrategies(ApplicationContext context) { 6     initMultipartResolver(context); 7     initLocaleResolver(context); 8     initThemeResolver(context); 9     initHandlerMappings(context);10     initHandlerAdapters(context);11     initHandlerExceptionResolvers(context);12     initRequestToViewNameTranslator(context);13     initViewResolvers(context);14     initFlashMapManager(context);15   }
复制代码

 


ContextLoaderListener初始化的是 WebApplicationContext, 创建后可以从ServletContext中获取,WebApplicationContext是应用程序内共享的,最多只有一个,如果寻求简单也可以不初始化此容器。与之不同 DispatcherServlet可以有多个,并共享一个WebApplicationContext容器,每一个DispatcherServlet有不同的配置,控制不同的WEB访问。一般将 DAO、Service 层Bean共享的放在ContextLoaderListener配置的容器中,将WEB层的Bean放在特定的DispatcherServlet配置的容器中。

SpringMVC利用Spring的注入特性初始化资源文件,只需要调用setPropertyValues方法就可将contextConfigLocation属性设置到对应实例中,也就是以依赖注入的方式初始化属性。

时序图如下(盗图):




请求处理流程

官网上的图



涉及到核心类与接口的过程描述:

客户端浏览器发送http请求,被`DispatcherServlet`捕获,调用关键的doDispatch方法,遍历所有注册为`Controller`的bean,为请求寻找关联映射,其中遍历查找的函数getHandler和getHandlerAdapter的源码:

复制代码
 1 /** 2   * Return the HandlerExecutionChain for this request. 3   * <p>Tries all handler mappings in order. 4   * @param request current HTTP request 5   * @return the HandlerExecutionChain, or {@code null} if no handler could be found 6   */ 7   protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { 8     for (HandlerMapping hm : this.handlerMappings) { 9       if (logger.isTraceEnabled()) {10         logger.trace(11             "Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");12       }13       HandlerExecutionChain handler = hm.getHandler(request);14       if (handler != null) {15         return handler;16       }17     }18     return null;19   }20   21   22   /**23   * Return the HandlerAdapter for this handler object.24   * @param handler the handler object to find an adapter for25   * @throws ServletException if no HandlerAdapter can be found for the handler. This is a fatal error.26   */27   protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {28     for (HandlerAdapter ha : this.handlerAdapters) {29       if (logger.isTraceEnabled()) {30         logger.trace("Testing handler adapter [" + ha + "]");31       }32       if (ha.supports(handler)) {33         return ha;34       }35     }36     throw new ServletException("No adapter for handler [" + handler +37         "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");38   }
复制代码

 

 



找到匹配的映射后`HandlerAdapter`会依次调用preHandle、handle(返回ModelAndView)、postHandle方法,所有步骤完成后调用processDispatchResult函数处理结果,并返回View给客户端。postDispatchResult函数和其中调用的render函数源码如下:

复制代码
 1 /** 2   * Handle the result of handler selection and handler invocation, which is 3   * either a ModelAndView or an Exception to be resolved to a ModelAndView. 4   */ 5   private void processDispatchResult(HttpServletRequest request, HttpServletResponse response, 6       HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception { 7 8     boolean errorView = false; 9 10     if (exception != null) {11       if (exception instanceof ModelAndViewDefiningException) {12         logger.debug("ModelAndViewDefiningException encountered", exception);13         mv = ((ModelAndViewDefiningException) exception).getModelAndView();14       }15       else {16         Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);17         mv = processHandlerException(request, response, handler, exception);18         errorView = (mv != null);19       }20     }21 22     // Did the handler return a view to render?23     if (mv != null && !mv.wasCleared()) {24       render(mv, request, response);25       if (errorView) {26         WebUtils.clearErrorRequestAttributes(request);27       }28     }29     else {30       if (logger.isDebugEnabled()) {31         logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() +32             "': assuming HandlerAdapter completed request handling");33       }34     }35 36     if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {37       // Concurrent handling started during a forward38       return;39     }40 41     if (mappedHandler != null) {42       mappedHandler.triggerAfterCompletion(request, response, null);43     }44   }45 46 47   /**48   * Render the given ModelAndView.49   * <p>This is the last stage in handling a request. It may involve resolving the view by name.50   * @param mv the ModelAndView to render51   * @param request current HTTP servlet request52   * @param response current HTTP servlet response53   * @throws ServletException if view is missing or cannot be resolved54   * @throws Exception if there's a problem rendering the view55   */56   protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {57     // Determine locale for request and apply it to the response.58     Locale locale = this.localeResolver.resolveLocale(request);59     response.setLocale(locale);60 61     View view;62     if (mv.isReference()) {63       // We need to resolve the view name.64       view = resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request);65       if (view == null) {66         throw new ServletException("Could not resolve view with name '" + mv.getViewName() +67             "' in servlet with name '" + getServletName() + "'");68       }69     }70     else {71       // No need to lookup: the ModelAndView object contains the actual View object.72       view = mv.getView();73       if (view == null) {74         throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a " +75             "View object in servlet with name '" + getServletName() + "'");76       }77     }78 79     // Delegate to the View object for rendering.80     if (logger.isDebugEnabled()) {81       logger.debug("Rendering view [" + view + "] in DispatcherServlet with name '" + getServletName() + "'");82     }83     try {84       if (mv.getStatus() != null) {85         response.setStatus(mv.getStatus().value());86       }87       view.render(mv.getModelInternal(), request, response);88     }89     catch (Exception ex) {90       if (logger.isDebugEnabled()) {91         logger.debug("Error rendering view [" + view + "] in DispatcherServlet with name '" +92             getServletName() + "'", ex);93       }94       throw ex;95     }96   }
复制代码

 


这就是一个完整的处理http请求的过程。盗图一张:

 

时序图如下(来源:http://neoremind.com/2016/02/springmvc%E7%9A%84%E4%B8%80%E4%BA%9B%E5%B8%B8%E7%94%A8%E6%9C%80%E4%BD%B3%E5%AE%9E%E8%B7%B5/):



配置实例


这里放的是最简单的配置,可以通过这个简单的配置实例回顾一下上面的过程。

目录结构

    -SpringMVCDemo
        -src
            -me.cyan
                -WelcomeController
        -web
            -WEB-INF
                -applicationContext.                -dispatcher-servlet.                -web.            -index.jsp
        -pom.        
pom.
引入的包

复制代码
 1 <?复制代码

 


web.

复制代码
 1 <?复制代码

 

 



dispatcher-servlet.

复制代码
 1 <?复制代码

 

 



WelcomeController

 

复制代码
 1 package me.cyan; 2 3 import org.springframework.stereotype.Controller; 4 import org.springframework.web.bind.annotation.RequestMapping; 5 import org.springframework.web.bind.annotation.ResponseBody; 6 7 /** 8 * Created by cyan on 16/5/23. 9 */10 11 @Controller12 public class welcomeController {13 14   @RequestMapping("/hello")15   public @ResponseBody String sayhello(){16     return "hello Spring MVC!";17   }18 }
复制代码

 

 

运行结果

 

 

文章出自:http://www.cnblogs.com/verlen11/p/5521536.html