核心类与接口
- 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
原标题:SpringMVC深入理解
关键词:Spring