wuyabing 2012-03-16
MVC架构
MVC是模型(model),视图(view),控制器(controller)3个单词的首字母缩写。有些应用需要处理用户请求并操纵和显示数据,MVC模式可以简化其实现。该模式由3个组件组成:
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"> <!-- Processes application requests --> <servlet> <servlet-name>appServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/spring/appServlet/servlet-context.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>appServlet</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> <!-- Disables Servlet Container welcome file handling. Needed for compatibility with Servlet 3.0 and Tomcat 7.0 --> <welcome-file-list> <welcome-file></welcome-file> </welcome-file-list> </web-app>
package org.springframework.samples.mvc.simple; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; @Controller public class SimpleController { @RequestMapping("/simple") @ResponseBody public String simple() { return "Hello world!"; } }
INFO: Mapped URL path [/simple] onto handler 'simpleController' INFO: Mapped URL path [/simple.*] onto handler 'simpleController' INFO: Mapped URL path [/simple/] onto handler 'simpleController'
@ResponseBody注解和@RequestBody相似。这个注解可以放在方法上,表明返回的类型应该是可以直接写入到HTTP响应体里的(Model中不会放置该值,该值也不会被转成一个view名字)。
<servlet> <servlet-name>appServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/spring/appServlet/servlet-context.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet>
public class DispatcherServlet extends FrameworkServlet
public abstract class FrameworkServlet extends HttpServletBean
public abstract class HttpServletBean extends HttpServlet implements EnvironmentAware
可见DispatcherServlet是继承了javax.servlet.http.HttpServlet的一个Servlet,既然是一个Servlet,则必然有
@Override public final void init() throws ServletException; @Override protected final void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException; @Override protected final void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException; @Override protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception;
这几个方法的实现,我们一个一个看:
init()方法在HttpServletBean中实现,doGet和doPost方法在FrameworkServlet中实现,doService()方法在DispatcherServlet中实现。
根据Servlet规范,一个Servlet启动的时候需要执行init()方法,一个请求来了首先判断是Get还Post方式提交,Get方式提交的执行doGet()方法,Post方式提交的执行doPost()方法。然后在doGet()和doPost()方法中可调用doService()方法,SpringMVC完全参照此规范。
在init()中,HttpServletBean执行
protected void initServletBean() throws ServletException { }
讲具体的实现交给FrameworkServlt重载的方法完成
@Override protected final void initServletBean() throws ServletException { System.out.println("使用servlet上下文记录日志"); getServletContext().log("Initializing Spring FrameworkServlet '" + getServletName() + "'"); if (this.logger.isInfoEnabled()) { this.logger.info("FrameworkServlet '" + getServletName() + "': initialization started"); } long startTime = System.currentTimeMillis(); System.out.println("启动时间:[" + startTime + "]"); try { System.out.println("initWebApplicationContext()"); this.webApplicationContext = initWebApplicationContext(); System.out.println("initFrameworkServlet(),这是一个空实现"); initFrameworkServlet(); } catch (ServletException ex) { this.logger.error("Context initialization failed", ex); throw ex; } catch (RuntimeException ex) { this.logger.error("Context initialization failed", ex); throw ex; } if (this.logger.isInfoEnabled()) { long elapsedTime = System.currentTimeMillis() - startTime; System.out.println("结束时间:[" + elapsedTime + "]"); this.logger.info("FrameworkServlet '" + getServletName() + "': initialization completed in " + elapsedTime + " ms"); } }
该方法会记录启动耗费的时间,也就时我们在启动项目时在控制台看到的时间,这里还解释了,为什么输出启动耗时的日志一般是红色的。
在该方法中执行了initWebApplicationContext()方法,进入该方法
protected WebApplicationContext initWebApplicationContext() { //System.out.println("使用WebApplicationContextUtils工具类得到WebApplicatonContext"); WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext()); WebApplicationContext wac = null; //System.out.println("判断webApplicationContext是否为null"); if (this.webApplicationContext != null) { //System.out.println("webApplicationContext非空!"); // A context instance was injected at construction time -> use it wac = this.webApplicationContext; if (wac instanceof ConfigurableWebApplicationContext) { ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac; if (!cwac.isActive()) { // The context has not yet been refreshed -> provide services such as // setting the parent context, setting the application context id, etc if (cwac.getParent() == null) { // The context instance was injected without an explicit parent -> set // the root application context (if any; may be null) as the parent cwac.setParent(rootContext); } configureAndRefreshWebApplicationContext(cwac); } } } //System.out.println("判断wac是否为null"); if (wac == null) { //System.out.println("was为空"); // No context instance was injected at construction time -> see if one // has been registered in the servlet context. If one exists, it is assumed // that the parent context (if any) has already been set and that the // user has performed any initialization such as setting the context id wac = findWebApplicationContext(); } //System.out.println("再次判断wac是否为null"); if (wac == null) { System.out.println("wac还为空"); System.out.println("No context instance is defined for this servlet -> create a local one"); // No context instance is defined for this servlet -> create a local one wac = createWebApplicationContext(rootContext); } System.out.println("this.refreshEventReceived:[" + this.refreshEventReceived + "]"); if (!this.refreshEventReceived) { System.out.println("onRefresh"); // Either the context is not a ConfigurableApplicationContext with refresh // support or the context injected at construction time had already been // refreshed -> trigger initial onRefresh manually here. onRefresh(wac); } System.out.println("this.publishContext:[" + this.publishContext + "]"); if (this.publishContext) { System.out.println("Publish the context as a servlet context attribute."); // Publish the context as a servlet context attribute. String attrName = getServletContextAttributeName(); getServletContext().setAttribute(attrName, wac); if (this.logger.isDebugEnabled()) { this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() + "' as ServletContext attribute with name [" + attrName + "]"); } } return wac; }
发现很长,但其实有用的只有wac = createWebApplicationContext(rootContext);在进入这个方法
protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) { System.out.println("createWebApplicationContext"); Class<?> contextClass = getContextClass(); if (this.logger.isDebugEnabled()) { this.logger.debug("Servlet with name '" + getServletName() + "' will try to create custom WebApplicationContext context of class '" + contextClass.getName() + "'" + ", using parent context [" + parent + "]"); } if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) { throw new ApplicationContextException( "Fatal initialization error in servlet with name '" + getServletName() + "': custom WebApplicationContext class [" + contextClass.getName() + "] is not of type ConfigurableWebApplicationContext"); } System.out.println("初始化Bean开始"); ConfigurableWebApplicationContext wac = (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass); System.out.println("初始化Bean结束"); wac.setParent(parent); wac.setConfigLocation(getContextConfigLocation()); System.out.println("configureAndRefreshWebApplicationContext开始"); configureAndRefreshWebApplicationContext(wac); System.out.println("configureAndRefreshWebApplicationContext结束"); return wac; }
于是我们就看到了有点眼熟的
ConfigurableWebApplicationContext wac = (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
这个方法就开始初始化我们servlet上下文即spring配置文件中的bean了。
我们的配置文件中servlet-context.xml只要一行
<beans:import resource="controllers.xml" />
而controllers.xml中
<context:component-scan base-package="org.springframework.samples.mvc" />
含义是扫描指定包下面所有带有注解的类,并将他们初始化。上面已经列出了带有@Controller注解的SimpleController类,于是Spring默认将这个bean的名字命名为simpleController(类名首字母小写),然后发现有一个方法带有@RequestMapping,Spring便通过org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping创建URL和控制器的映射(handlerMapping),具体过程可看如下日志:
DEBUG: org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping - Looking for URL mappings in application context: WebApplicationContext for namespace 'appServlet-servlet': startup date [Fri Mar 16 05:36:03 CST 2012]; root of context hierarchy DEBUG: org.springframework.beans.factory.support.DefaultListableBeanFactory - Returning cached instance of singleton bean 'simpleController' 将一个URL路径映射到一个handler上 INFO : org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping - Mapped URL path [/simple] onto handler 'simpleController' DEBUG: org.springframework.beans.factory.support.DefaultListableBeanFactory - Returning cached instance of singleton bean 'simpleController' 将一个URL路径映射到一个handler上 INFO : org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping - Mapped URL path [/simple.*] onto handler 'simpleController' DEBUG: org.springframework.beans.factory.support.DefaultListableBeanFactory - Returning cached instance of singleton bean 'simpleController' 将一个URL路径映射到一个handler上 INFO : org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping - Mapped URL path [/simple/] onto handler 'simpleController'
URL到控制器的映射创建完了,剩下的便是处理器代理了(handlerAdapter),前文提到了,DispatcherServlet有一个List类型的handlerAdapters,如果为null,系统将使用默认策略。这个默认策略体现在DispatcherServlet的这个方法中
/** * Initialize the HandlerAdapters used by this class. * <p>If no HandlerAdapter beans are defined in the BeanFactory for this namespace, * we default to SimpleControllerHandlerAdapter. */ private void initHandlerAdapters(ApplicationContext context) { this.handlerAdapters = null; if (this.detectAllHandlerAdapters) { // Find all HandlerAdapters in the ApplicationContext, including ancestor contexts. Map<String, HandlerAdapter> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerAdapter.class, true, false); if (!matchingBeans.isEmpty()) { this.handlerAdapters = new ArrayList<HandlerAdapter>(matchingBeans.values()); // We keep HandlerAdapters in sorted order. OrderComparator.sort(this.handlerAdapters); } } else { try { HandlerAdapter ha = context.getBean(HANDLER_ADAPTER_BEAN_NAME, HandlerAdapter.class); this.handlerAdapters = Collections.singletonList(ha); } catch (NoSuchBeanDefinitionException ex) { // Ignore, we'll add a default HandlerAdapter later. } } // Ensure we have at least some HandlerAdapters, by registering // default HandlerAdapters if no other adapters are found. if (this.handlerAdapters == null) { System.out.println("确保我们有一些handlerAdapters"); this.handlerAdapters = getDefaultStrategies(context, HandlerAdapter.class); if (logger.isDebugEnabled()) { logger.debug("No HandlerAdapters found in servlet '" + getServletName() + "': using default"); } } }
这个默认策略会给我们三个HandlerAdapter,具体日志:
DEBUG: org.springframework.beans.factory.support.DefaultListableBeanFactory - Finished creating instance of bean 'org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter' DEBUG: org.springframework.beans.factory.support.DefaultListableBeanFactory - Finished creating instance of bean 'org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter' DEBUG: org.springframework.beans.factory.support.DefaultListableBeanFactory - Finished creating instance of bean 'org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter'
至此,init()可以算是执行完了(当然为了简化,还有一些无关大局的细节略去没有讲),我们的Servlet,也就是项目就运行起来了。
下面讲解请求部分
先来看一段浏览器发出请求后,服务端的记录的日志
FrameworkServlet doGet TRACE: org.springframework.web.servlet.DispatcherServlet - Bound request context to thread: [GET /web/simple]@902324502 org.eclipse.jetty.server.Request@35c86116 doService DEBUG: org.springframework.web.servlet.DispatcherServlet - DispatcherServlet with name 'appServlet' processing GET request for [/web/simple] doDispatch Determine handler for the current request. TRACE: org.springframework.web.servlet.DispatcherServlet - Testing handler map [org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping@20d1fa4] in DispatcherServlet with name 'appServlet' TRACE: org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping - No handler mapping found for [/simple] TRACE: org.springframework.web.servlet.DispatcherServlet - Testing handler map [org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping@5b33a7af] in DispatcherServlet with name 'appServlet' DEBUG: org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping - Mapping [/simple] to HandlerExecutionChain with handler [org.springframework.samples.mvc.simple.SimpleController@573b7064] and 1 interceptor Determine handler adapter for the current request. TRACE: org.springframework.web.servlet.DispatcherServlet - Testing handler adapter [org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter@557ce3bb] TRACE: org.springframework.web.servlet.DispatcherServlet - Testing handler adapter [org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter@6996a298] TRACE: org.springframework.web.servlet.DispatcherServlet - Testing handler adapter [org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter@783b67a7] Process last-modified header, if supported by the handler. DEBUG: org.springframework.web.servlet.DispatcherServlet - Last-Modified value for [/web/simple] is: -1 Apply preHandle methods of registered interceptors. Actually invoke the handler. class org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter invokeHandlerMethod 真正处理 DEBUG: org.springframework.web.bind.annotation.support.HandlerMethodInvoker - Invoking request handler method: public java.lang.String org.springframework.samples.mvc.simple.SimpleController.simple() result:[Hello world!] getModelAndView[class java.lang.String] 原来进入了这个分支 OK搞定 具体输出数据 DEBUG: org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter - Written [Hello world!] as "text/html" using [org.springframework.http.converter.StringHttpMessageConverter@6b7de5ce] 返回mv为空,假设handler已经处理完了 Do we need view name translation? Apply postHandle methods of registered interceptors. DEBUG: org.springframework.web.servlet.DispatcherServlet - Null ModelAndView returned to DispatcherServlet with name 'appServlet': assuming HandlerAdapter completed request handling Trigger after-completion for successful outcome. [1] TRACE: org.springframework.web.servlet.DispatcherServlet - Cleared thread-bound request context: [GET /web/simple]@902324502 org.eclipse.jetty.server.Request@35c86116 DEBUG: org.springframework.web.servlet.DispatcherServlet - Successfully completed request TRACE: org.springframework.web.context.support.XmlWebApplicationContext - Publishing event in WebApplicationContext for namespace 'appServlet-servlet': ServletRequestHandledEvent: url=[/web/simple]; client=[0:0:0:0:0:0:0:1%0]; method=[GET]; servlet=[appServlet]; session=[null]; user=[null]; time=[154ms]; status=[OK]
我们来详细分析一下,请求上来时,首先进入FrameworkServlet的doGet方法,然后调用
/** * Process this request, publishing an event regardless of the outcome. * <p>The actual event handling is performed by the abstract * {@link #doService} template method. */ protected final void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { long startTime = System.currentTimeMillis(); Throwable failureCause = null; // Expose current LocaleResolver and request as LocaleContext. LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext(); LocaleContextHolder.setLocaleContext(buildLocaleContext(request), this.threadContextInheritable); // Expose current RequestAttributes to current thread. RequestAttributes previousRequestAttributes = RequestContextHolder.getRequestAttributes(); ServletRequestAttributes requestAttributes = null; if (previousRequestAttributes == null || previousRequestAttributes.getClass().equals(ServletRequestAttributes.class)) { requestAttributes = new ServletRequestAttributes(request); RequestContextHolder.setRequestAttributes(requestAttributes, this.threadContextInheritable); } if (logger.isTraceEnabled()) { logger.trace("Bound request context to thread: " + request); } try { doService(request, response); } catch (ServletException ex) { failureCause = ex; throw ex; } catch (IOException ex) { failureCause = ex; throw ex; } catch (Throwable ex) { failureCause = ex; throw new NestedServletException("Request processing failed", ex); } finally { // Clear request attributes and reset thread-bound context. LocaleContextHolder.setLocaleContext(previousLocaleContext, this.threadContextInheritable); if (requestAttributes != null) { RequestContextHolder.setRequestAttributes(previousRequestAttributes, this.threadContextInheritable); requestAttributes.requestCompleted(); } if (logger.isTraceEnabled()) { logger.trace("Cleared thread-bound request context: " + request); } if (logger.isDebugEnabled()) { if (failureCause != null) { this.logger.debug("Could not complete request", failureCause); } else { this.logger.debug("Successfully completed request"); } } if (this.publishEvents) { // Whether or not we succeeded, publish an event. long processingTime = System.currentTimeMillis() - startTime; this.webApplicationContext.publishEvent( new ServletRequestHandledEvent(this, request.getRequestURI(), request.getRemoteAddr(), request.getMethod(), getServletConfig().getServletName(), WebUtils.getSessionId(request), getUsernameForRequest(request), processingTime, failureCause)); } } }
该方法很杂,其他的不要看,就看调用了doService方法即可。此doService由DispatcherServlet重写
@Override protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception { System.out.println("doService"); if (logger.isDebugEnabled()) { String requestUri = urlPathHelper.getRequestUri(request); logger.debug("DispatcherServlet with name '" + getServletName() + "' processing " + request.getMethod() + " request for [" + requestUri + "]"); } // Keep a snapshot of the request attributes in case of an include, // to be able to restore the original attributes after the include. Map<String, Object> attributesSnapshot = null; if (WebUtils.isIncludeRequest(request)) { logger.debug("Taking snapshot of request attributes before include"); 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)); } } } this.flashMapManager.requestStarted(request); // Make framework objects available to handlers and view objects. 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()); try { doDispatch(request, response); } finally { this.flashMapManager.requestCompleted(request); // Restore the original attribute snapshot, in case of an include. if (attributesSnapshot != null) { restoreAttributesAfterInclude(request, attributesSnapshot); } } }
其他不要看,就看调用了doDispatch方法
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { System.out.println("doDispatch"); HttpServletRequest processedRequest = request; HandlerExecutionChain mappedHandler = null; int interceptorIndex = -1; try { ModelAndView mv; boolean errorView = false; try { processedRequest = checkMultipart(request); // Determine handler for the current request. System.out.println("Determine handler for the current request."); mappedHandler = getHandler(processedRequest, false); if (mappedHandler == null || mappedHandler.getHandler() == null) { noHandlerFound(processedRequest, response); return; } // Determine handler adapter for the current request. System.out.println("Determine handler adapter for the current request."); HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); // Process last-modified header, if supported by the handler. System.out.println("Process last-modified header, if supported by the handler."); String method = request.getMethod(); boolean isGet = "GET".equals(method); if (isGet || "HEAD".equals(method)) { long lastModified = ha.getLastModified(request, mappedHandler.getHandler()); if (logger.isDebugEnabled()) { String requestUri = urlPathHelper.getRequestUri(request); logger.debug("Last-Modified value for [" + requestUri + "] is: " + lastModified); } if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) { return; } } // Apply preHandle methods of registered interceptors. System.out.println("Apply preHandle methods of registered interceptors."); HandlerInterceptor[] interceptors = mappedHandler.getInterceptors(); if (interceptors != null) { for (int i = 0; i < interceptors.length; i++) { HandlerInterceptor interceptor = interceptors[i]; if (!interceptor.preHandle(processedRequest, response, mappedHandler.getHandler())) { triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null); return; } interceptorIndex = i; } } // Actually invoke the handler. System.out.println("Actually invoke the handler."); mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); System.out.println("返回mv为空,假设handler已经处理完了"); // Do we need view name translation? System.out.println("Do we need view name translation?"); if (mv != null && !mv.hasView()) { mv.setViewName(getDefaultViewName(request)); } // Apply postHandle methods of registered interceptors. System.out.println("Apply postHandle methods of registered interceptors."); if (interceptors != null) { for (int i = interceptors.length - 1; i >= 0; i--) { HandlerInterceptor interceptor = interceptors[i]; interceptor.postHandle(processedRequest, response, mappedHandler.getHandler(), mv); } } } catch (ModelAndViewDefiningException ex) { logger.debug("ModelAndViewDefiningException encountered", ex); mv = ex.getModelAndView(); } catch (Exception ex) { Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null); mv = processHandlerException(processedRequest, response, handler, ex); errorView = (mv != null); } // Did the handler return a view to render? if (mv != null && !mv.wasCleared()) { System.out.println("执行render了"); render(mv, processedRequest, response); if (errorView) { WebUtils.clearErrorRequestAttributes(request); } } else { if (logger.isDebugEnabled()) { logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() + "': assuming HandlerAdapter completed request handling"); } } // Trigger after-completion for successful outcome. System.out.println("Trigger after-completion for successful outcome."); triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null); } catch (Exception ex) { // Trigger after-completion for thrown exception. triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, ex); throw ex; } catch (Error err) { ServletException ex = new NestedServletException("Handler processing failed", err); // Trigger after-completion for thrown exception. triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, ex); throw ex; } finally { // Clean up any resources used by a multipart request. if (processedRequest != request) { cleanupMultipart(processedRequest); } } }
这里才是最精髓的,代码
System.out.println("Determine handler for the current request.");
对应日志部分
Determine handler for the current request. TRACE: org.springframework.web.servlet.DispatcherServlet - Testing handler map [org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping@20d1fa4] in DispatcherServlet with name 'appServlet' TRACE: org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping - No handler mapping found for [/simple] TRACE: org.springframework.web.servlet.DispatcherServlet - Testing handler map [org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping@5b33a7af] in DispatcherServlet with name 'appServlet' DEBUG: org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping - Mapping [/simple] to HandlerExecutionChain with handler [org.springframework.samples.mvc.simple.SimpleController@573b7064] and 1 interceptor
代码
Determine handler adapter for the current request.
对应日志部分
Determine handler adapter for the current request. TRACE: org.springframework.web.servlet.DispatcherServlet - Testing handler adapter [org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter@557ce3bb] TRACE: org.springframework.web.servlet.DispatcherServlet - Testing handler adapter [org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter@6996a298] TRACE: org.springframework.web.servlet.DispatcherServlet - Testing handler adapter [org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter@783b67a7]
handlerMapping测试了BeanNameUrlHandlerMapping发现没有,接着找DefaultAmmotationHandlerMapping,有对应的映射,搞定。
handlerAdapter测试了HttpRequestHandlerAdapter不行,SimpleControllerHandlerAdapter不行,最后找到AnnotationMethodHandlerAdapter,有对应关系,搞定。
下面来到这段代码
System.out.println("Actually invoke the handler."); mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); System.out.println("返回mv为空,假设handler已经处理完了");
注意这里的mappedHandler.geteHandler()就是一个AnnotationMethodHandlerAdapter。
再调用
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println(getClass()); Class<?> clazz = ClassUtils.getUserClass(handler); Boolean annotatedWithSessionAttributes = this.sessionAnnotatedClassesCache.get(clazz); if (annotatedWithSessionAttributes == null) { annotatedWithSessionAttributes = (AnnotationUtils.findAnnotation(clazz, SessionAttributes.class) != null); this.sessionAnnotatedClassesCache.put(clazz, annotatedWithSessionAttributes); } if (annotatedWithSessionAttributes) { // Always prevent caching in case of session attribute management. checkAndPrepare(request, response, this.cacheSecondsForSessionAttributeHandlers, true); // Prepare cached set of session attributes names. } else { // Uses configured default cacheSeconds setting. checkAndPrepare(request, response, true); } // Execute invokeHandlerMethod in synchronized block if required. if (this.synchronizeOnSession) { System.out.println("同步"); HttpSession session = request.getSession(false); if (session != null) { Object mutex = WebUtils.getSessionMutex(session); synchronized (mutex) { return invokeHandlerMethod(request, response, handler); } } } return invokeHandlerMethod(request, response, handler); }
其他的无视,看invokeHandlerMethod方法
protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("invokeHandlerMethod"); ServletHandlerMethodResolver methodResolver = getMethodResolver(handler); Method handlerMethod = methodResolver.resolveHandlerMethod(request); ServletHandlerMethodInvoker methodInvoker = new ServletHandlerMethodInvoker(methodResolver); ServletWebRequest webRequest = new ServletWebRequest(request, response); ExtendedModelMap implicitModel = new BindingAwareModelMap(); Object result = methodInvoker.invokeHandlerMethod(handlerMethod, handler, webRequest, implicitModel); System.out.println("result:[" + result + "]"); ModelAndView mav = methodInvoker.getModelAndView(handlerMethod, handler.getClass(), result, implicitModel, webRequest); methodInvoker.updateModelAttributes(handler, (mav != null ? mav.getModel() : null), implicitModel, webRequest); return mav; }
进入 getModelAndView方法
public ModelAndView getModelAndView(Method handlerMethod, Class handlerType, Object returnValue, ExtendedModelMap implicitModel, ServletWebRequest webRequest) throws Exception { System.out.println("getModelAndView[" + returnValue.getClass() + "]"); ResponseStatus responseStatusAnn = AnnotationUtils.findAnnotation(handlerMethod, ResponseStatus.class); if (responseStatusAnn != null) { HttpStatus responseStatus = responseStatusAnn.value(); String reason = responseStatusAnn.reason(); if (!StringUtils.hasText(reason)) { webRequest.getResponse().setStatus(responseStatus.value()); } else { webRequest.getResponse().sendError(responseStatus.value(), reason); } // to be picked up by the RedirectView webRequest.getRequest().setAttribute(View.RESPONSE_STATUS_ATTRIBUTE, responseStatus); responseArgumentUsed = true; } // Invoke custom resolvers if present... if (customModelAndViewResolvers != null) { System.out.println("Invoke custom resolvers if present..."); for (ModelAndViewResolver mavResolver : customModelAndViewResolvers) { ModelAndView mav = mavResolver.resolveModelAndView( handlerMethod, handlerType, returnValue, implicitModel, webRequest); if (mav != ModelAndViewResolver.UNRESOLVED) { return mav; } } } if (returnValue instanceof HttpEntity) { handleHttpEntityResponse((HttpEntity<?>) returnValue, webRequest); return null; } else if (AnnotationUtils.findAnnotation(handlerMethod, ResponseBody.class) != null) { System.out.println("原来进入了这个分支"); handleResponseBody(returnValue, webRequest); return null; } else if (returnValue instanceof ModelAndView) { ModelAndView mav = (ModelAndView) returnValue; mav.getModelMap().mergeAttributes(implicitModel); return mav; } else if (returnValue instanceof Model) { return new ModelAndView().addAllObjects(implicitModel).addAllObjects(((Model) returnValue).asMap()); } else if (returnValue instanceof View) { return new ModelAndView((View) returnValue).addAllObjects(implicitModel); } else if (AnnotationUtils.findAnnotation(handlerMethod, ModelAttribute.class) != null) { addReturnValueAsModelAttribute(handlerMethod, handlerType, returnValue, implicitModel); return new ModelAndView().addAllObjects(implicitModel); } else if (returnValue instanceof Map) { return new ModelAndView().addAllObjects(implicitModel).addAllObjects((Map) returnValue); } else if (returnValue instanceof String) { System.out.println("返回值为String"); return new ModelAndView((String) returnValue).addAllObjects(implicitModel); } else if (returnValue == null) { // Either returned null or was 'void' return. if (this.responseArgumentUsed || webRequest.isNotModified()) { return null; } else { // Assuming view name translation... return new ModelAndView().addAllObjects(implicitModel); } } else if (!BeanUtils.isSimpleProperty(returnValue.getClass())) { // Assume a single model attribute... addReturnValueAsModelAttribute(handlerMethod, handlerType, returnValue, implicitModel); return new ModelAndView().addAllObjects(implicitModel); } else { throw new IllegalArgumentException("Invalid handler method return value: " + returnValue); } }
于是我们看到了熟悉的ResponseBody.class的身影
然后handleResponseBody方法
private void handleResponseBody(Object returnValue, ServletWebRequest webRequest) throws Exception { System.out.println("OK搞定"); if (returnValue == null) { return; } HttpInputMessage inputMessage = createHttpInputMessage(webRequest); HttpOutputMessage outputMessage = createHttpOutputMessage(webRequest); writeWithMessageConverters(returnValue, inputMessage, outputMessage); }
可以看到就是在这个方法中直接将字符串发送到浏览器的。writeWithMessageConverters方法无关紧要就不要列出来了。
呼,我是为了完整性,才将方法整个拷贝下来了,其实我们只需要关注主要的一行调用代码即可。
总结一下?
我觉得原理知道即可,关键还是会用,会搭建。下面讲一下如何搭建这个sample。
老办法,从GitHub上将代码clone下来加载到IDE中。
git clone git://github.com/stephansun/samples.git
最后补充一下pom.xml
<dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>3.1.0.RELEASE</version> </dependency> </dependencies>
仅仅加载了spring-webmvc,打开m2e的Dependency Hierarchy栏,观察spring模块间的依赖关系:
spring-webmvc
|- spring-asm
|- spring-beans
|- spring-context
|- spring-context-support
|- spring-core
|- spring-expression
|- spring-web
spring-beans
|- spring-core
spring-context
|- spring-aop
|- spring-beans
|- spring-core
|- spring-expression
|- spring-asm
spring-context-support
|- spring-beans
|- spring-context
|- spring-core
spring-core
|- spring-asm
|- commons-logging
spring-expression
|- spring-core
spring-web
|- aoplliance
|- spring-beans
|- spring-context
|- spring-core
Spring发布包