SpringMVC配置详解 配置前端控制器DispatcherServletSpringMVC是一个基于DispatcherServlet的MVC框架,每一个请求最先访问的都是DispatcherServlet,DispatcherServlet是继承自HttpServlet的,DispatcherServlet负责转发每一个Request请求给相应的Handler,Handler处理以后再返回相应的视图(View)和模型(Model),返回的视图和模型都可以不指定,即可以只返回Model或只返回View或都不返回。
首先,在web.xml文件中声明DispatcherServlet:
web.xml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 <?xml version="1.0" encoding="UTF-8" ?> <web-app xmlns ="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation ="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" version ="3.1" > <listener > <listener-class > org.springframework.web.context.ContextLoaderListener</listener-class > </listener > <servlet > <servlet-name > dispatcher</servlet-name > <servlet-class > org.springframework.web.servlet.DispatcherServlet</servlet-class > <init-param > <param-name > contextConfigLocation</param-name > <param-value > classpath:spring/spring-mvc.xml</param-value > </init-param > <load-on-startup > 1</load-on-startup > <async-supported > true</async-supported > </servlet > <servlet-mapping > <servlet-name > dispatcher</servlet-name > <url-pattern > *.action</url-pattern > </servlet-mapping > <welcome-file-list > <welcome-file > index.jsp</welcome-file > </welcome-file-list > </web-app >
配置处理器适配器、映射器、视图解析器等然后,在classpath下的spring-mvc.xml中配置处理器适配器:
spring-mvc.xml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 <?xml version="1.0" encoding="UTF-8" ?> <beans xmlns ="http://www.springframework.org/schema/beans" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xmlns:context ="http://www.springframework.org/schema/context" xsi:schemaLocation ="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd" > <bean id ="queryController" name ="/query.action" class ="com.syshlang.smm.controller.QueryController" > </bean > <bean id ="queryHttpController" name ="/httpquery.action" class ="com.syshlang.smm.controller.QueryHttpController" > </bean > <context:component-scan base-package ="com.syshlang.smm.controller" > </context:component-scan > <bean class ="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping" > </bean > <bean class ="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping" > <property name ="mappings" > <props > <prop key ="/query0.action" > queryController</prop > <prop key ="/query1.action" > queryController</prop > <prop key ="/httpquery0.action" > queryHttpController</prop > <prop key ="/httpquery1.action" > queryHttpController</prop > </props > </property > </bean > <bean class ="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping" > </bean > <bean class ="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter" > </bean > <bean class ="org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter" > </bean > <bean class ="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter" > </bean > <bean class ="org.springframework.web.servlet.view.InternalResourceViewResolver" > </bean > <bean id ="log4jInitialization" class ="org.springframework.beans.factory.config.MethodInvokingFactoryBean" > <property name ="targetClass" value ="org.springframework.util.Log4jConfigurer" /> <property name ="targetMethod" value ="initLogging" /> <property name ="arguments" > <list > <value > classpath:log4j.properties</value > </list > </property > </bean > </beans >
开发Handler在此处只举一例,如下:通过实现 controller接口,由org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter适配器执行的情况。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 package com.syshlang.smm.controller;import com.syshlang.smm.pojo.QueryPojo;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.web.servlet.ModelAndView;import org.springframework.web.servlet.mvc.Controller;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.util.ArrayList;import java.util.List;public class QueryController implements Controller { private static final Logger logger = LoggerFactory.getLogger(QueryController.class); @Override public ModelAndView handleRequest (HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception { List<QueryPojo> list = new ArrayList <QueryPojo>(); QueryPojo pojo = new QueryPojo (); pojo.setId("1" ); pojo.setName("aaaaaa" ); list.add(pojo); ModelAndView modelAndView = new ModelAndView (); modelAndView.addObject("listpojo" ,list); modelAndView.addObject("des" ,"使用SimpleControllerHandlerAdapter处理器适配器实现Controller接口的处理器" ); modelAndView.setViewName("/view/query.jsp" ); logger.info("the first jsp pages" ); return modelAndView; } }
源码分析 源码分析根据配置过程,通过前端控制器源码分析SpringMVC的执行过程。
第一步:前端控制器接收请求在org.springframework.web.servlet.DispatcherServlet中可以看到doDispatch方法。前端控制器接收请求,.action类型的URL通过过滤器进入DispatcherServlet类,doDispatch()方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 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 || mappedHandler.getHandler() == 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 (this .logger.isDebugEnabled()) { this .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 ; } 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); } } }
第二步:前端控制器调用处理器映射器查找 Handler在doDispatch()方法中调用了DispatcherServlet类的getHandler方法。映射器根据request当中的URL,找到了Handler,最终返回一个执行器的链(HandlerExecutionChain)。这个链里面有Handler。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 protected HandlerExecutionChain getHandler (HttpServletRequest request) throws Exception { Iterator var2 = this .handlerMappings.iterator(); HandlerExecutionChain handler; do { if (!var2.hasNext()) { return null ; } HandlerMapping hm = (HandlerMapping)var2.next(); if (this .logger.isTraceEnabled()) { this .logger.trace("Testing handler map [" + hm + "] in DispatcherServlet with name '" + this .getServletName() + "'" ); } handler = hm.getHandler(request); } while (handler == null ); return handler; }
第三步:调用处理器适配器执行Handler,得到执行结果ModelAndView1 2 3 ... mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); ...
第四步:视图渲染,将model数据填充到request域。视图解析,得到view: 在doDispatch()方法中
1 this .processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 private void processDispatchResult (HttpServletRequest request, HttpServletResponse response, HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception { boolean errorView = false ; if (exception != null ) { if (exception instanceof ModelAndViewDefiningException) { this .logger.debug("ModelAndViewDefiningException encountered" , exception); mv = ((ModelAndViewDefiningException)exception).getModelAndView(); } else { Object handler = mappedHandler != null ? mappedHandler.getHandler() : null ; mv = this .processHandlerException(request, response, handler, exception); errorView = mv != null ; } } if (mv != null && !mv.wasCleared()) { this .render(mv, request, response); if (errorView) { WebUtils.clearErrorRequestAttributes(request); } } else if (this .logger.isDebugEnabled()) { this .logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + this .getServletName() + "': assuming HandlerAdapter completed request handling" ); } if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) { if (mappedHandler != null ) { mappedHandler.triggerAfterCompletion(request, response, (Exception)null ); } } }
在processDispatchResulth()方法中
1 this .render(mv, request, response);
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 protected void render (ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception { Locale locale = this .localeResolver.resolveLocale(request); response.setLocale(locale); View view; if (mv.isReference()) { view = this .resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request); if (view == null ) { throw new ServletException ("Could not resolve view with name '" + mv.getViewName() + "' in servlet with name '" + this .getServletName() + "'" ); } } else { view = mv.getView(); if (view == null ) { throw new ServletException ("ModelAndView [" + mv + "] neither contains a view name nor a " + "View object in servlet with name '" + this .getServletName() + "'" ); } } if (this .logger.isDebugEnabled()) { this .logger.debug("Rendering view [" + view + "] in DispatcherServlet with name '" + this .getServletName() + "'" ); } try { if (mv.getStatus() != null ) { response.setStatus(mv.getStatus().value()); } view.render(mv.getModelInternal(), request, response); } catch (Exception var7) { if (this .logger.isDebugEnabled()) { this .logger.debug("Error rendering view [" + view + "] in DispatcherServlet with name '" + this .getServletName() + "'" , var7); } throw var7; } }
在render()方法中
1 view = this .resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request);
调用view的渲染方法,将model数据填充到request域
1 view.render(mv.getModelInternal(), request, response);
根据InternalResourceView的继承关系:org.springframework.web.servlet.view.AbstractView ,org.springframework.web.servlet.view.AbstractUrlBasedView,org.springframework.web.servlet.view.InternalResourceView 最终找到render方法在AbstractView中,如下代码所示:
1 2 3 4 5 6 7 8 9 public void render (Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception { if (this .logger.isTraceEnabled()) { this .logger.trace("Rendering view with name '" + this .beanName + "' with model " + model + " and static attributes " + this .staticAttributes); } Map<String, Object> mergedModel = this .createMergedOutputModel(model, request, response); this .prepareResponse(request, response); this .renderMergedOutputModel(mergedModel, this .getRequestToExpose(request), response); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 protected void exposeModelAsRequestAttributes (Map<String, Object> model, HttpServletRequest request) throws Exception { Iterator var3 = model.entrySet().iterator(); while (var3.hasNext()) { Entry<String, Object> entry = (Entry)var3.next(); String modelName = (String)entry.getKey(); Object modelValue = entry.getValue(); if (modelValue != null ) { request.setAttribute(modelName, modelValue); if (this .logger.isDebugEnabled()) { this .logger.debug("Added model object '" + modelName + "' of type [" + modelValue.getClass().getName() + "] to request in view with name '" + this .getBeanName() + "'" ); } } else { request.removeAttribute(modelName); if (this .logger.isDebugEnabled()) { this .logger.debug("Removed model object '" + modelName + "' from request in view with name '" + this .getBeanName() + "'" ); } } } }