- 浏览: 270415 次
- 性别:
- 来自: 上海
文章分类
最新评论
-
hongxingyaru_d5b032:
但是其中“这个Accept和Request Headers中的 ...
基于Spring MVC的Web应用开发(7) - Headers -
hongxingyaru_d5b032:
学习到了,感谢分享!
基于Spring MVC的Web应用开发(7) - Headers -
bo_hai:
总结的不错。
基于Spring MVC的Web应用开发(7) - Headers -
fendou3754:
那你也说一下spring-asm重新打包以后的源码怎么获取,尽 ...
spring将spring-asm重新打包原因 -
di1984HIT:
谢谢啊~学习了~~
JMS、AMQP实例讲解
MVC架构
MVC是模型(model),视图(view),控制器(controller)3个单词的首字母缩写。有些应用需要处理用户请求并操纵和显示数据,MVC模式可以简化其实现。该模式由3个组件组成:
- 模型表示用户期望看到的数据。通常情况下,模型由JavaBean组成。
- 视图负责显示模型。文本编辑器中的视图组件会以恰当的格式显示一段文本,视图在Web应用中会生成客户端浏览器可以解释显示的HTML。
- 控制器表示逻辑代码,负责处理请求和执行用户的意图。它会构建恰当的模型并将其传入视图进行显示。对Java Web应用来说,控制器多是一个servlet。当然,控制器可以使用任意语言实现,只要web容器支持就行。
<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发布包
- spring-aop.jar:此JAR文件包含了所有你在应用中使用Spring AOP特性时需要的类。如果应用中使用了其他涉及AOP的Spring功能时,例如声明式事务管理,你也需要将此JAR文件包含进来。
- spring-beans.jar:此文件包含了所有Spring依赖注入需要的代码。包括bean工厂和相关支持类。大部分情况下,你会需要加入spring-context.jar文件,它包含了建立应用环境上下文需要的代码
- spring-context.jar:此JAR文件包含了建立Spring应用环境上下文所需的代码,它将主要的ApplicationContext接口和实现、说明、JNDI、调度、主题和验证一起纳入其中
- spring-context-support.jar:这个包文件包括了Spring的工具代码,其中包括说明、电子邮件、调度支持以及脚本语言支持
- spring-core.jar:此文件包含了Spring框架的核心代码。它用来处理注解、枚举、任务执行、资源加载以及其他一些即便在Spring框架环境外也会有用的工具和异常类。
- spring-jdbc.jar:此文件包含了JDBC支持类的代码,例如JdbcTemplate类和JdbcDaoSupport类
- spring-jms.jar:此文件包含jms的代码
- spring-orm.jar:此文件包含了对象-关系映射(ORM)工具需要的文件。把这个包加入到classpath上将会给你提供针对Hibernate3,iBatis,JDO,JPA和TopLink的Spring支持
- spring-test.jar:此文件包含了使用Spring框架编写单元测试和集成测试的支持代码
- spring-tx.jar:此文件提供了核心的数据访问异常和事务技术支持。这两个概念彼此关系密切,因为一般情况下事务总同某些数据访问代码一起工作的
- spring-web.jar:此文件包含了Spring Web支持(工具类,绑定器,分段文件解析器)的代码
- spring-webmvc.jar:此文件包含了Spring MVC的代码
发表评论
-
《Pro Spring Integration》读书笔记
2012-06-02 11:30 0a -
基于Spring MVC的Web应用开发(12) - Form
2012-03-31 14:50 5959本节介绍SpringMVC中的表单,demo演示访问一个表单提 ... -
基于Spring MVC的Web应用开发(11) - Views
2012-03-31 13:13 8797在FileUpload一文中,我们初步了解了SpringMVC ... -
基于Spring MVC的Web应用开发(10) - Validation
2012-03-31 11:36 3168本文介绍SpringMVC的验证(validation),在C ... -
基于Spring MVC的Web应用开发(9) - Exceptions
2012-03-31 10:55 1762本文介绍SpringMVC中的异常处理,@Controller ... -
基于Spring MVC的Web应用开发(8) - Convert
2012-03-31 09:25 3672本文介绍SpringMVC中的Convert,Convert是 ... -
基于Spring MVC的Web应用开发(7) - Headers
2012-03-30 15:01 27353本文接上一篇文章,介 ... -
基于Spring MVC的Web应用开发(6) - Response
2012-03-30 10:56 36089本文讲解Spring MVC的Response,深入了解一下@ ... -
基于Spring MVC的Web应用开发(5) - Redirect
2012-03-26 15:10 17523本文介绍Spring MVC中的重定向(Redirect),先 ... -
基于Spring MVC的Web应用开发(4) - FileUpload
2012-03-20 21:17 35741上一篇文章介绍了Spring MVC如何处理静态资源文件,本文 ... -
基于Spring MVC的Web应用开发(3) - Resources
2012-03-20 16:22 6605上一篇介绍了在基于Spring MVC的Web项目中加入日志, ... -
基于Spring MVC的Web应用开发(2) - Log
2012-03-19 11:55 3589上一篇文章我们使用SpringMVC搭建了一个简单WEB项目 ... -
spring-oxm中unmashaller疑似bug
2012-03-08 10:21 2556spring-integration中测试XML Transf ... -
spring将spring-asm重新打包原因
2012-03-06 13:41 8275以前就发现通过eclipse上面的m2e插件不能将spring ... -
Spring底层使用SAX解析XML配置文件
2012-02-29 01:28 6118最近在研究Spring源代码,在梳理spring-beans时 ... -
在Spring 3.1.0 M2中配置Hibernate事务失效?
2011-10-22 00:38 2173SpringSource与2011年6月8号发布了Spring ...
相关推荐
在 `spring-mvc-helloworld` 项目中,可能会包含一个 `web.xml` 文件来配置 DispatcherServlet。此外,还需要 Spring MVC 的配置文件(如 `servlet-context.xml`),其中定义了 Controller、视图解析器和其他组件的...
Spring3MVC-REST-HelloWorld 是一个基础的示例,用于展示如何在Spring框架的MVC模块中实现RESTful Web服务。这个实例是初学者理解Spring MVC与REST结合使用的理想起点。REST(Representational State Transfer)是一...
Spring MVC 是一个基于 Java 的轻量级 Web 开发框架,它是 Spring 框架的一部分,主要用于构建 MVC(Model-View-Controller)模式的 Web 应用程序。在本"spring-mvc helloworld demo"中,我们可以看到如何设置一个...
理解并熟练运用这些基础概念,将为你进一步探索Spring MVC和更复杂的Web应用开发奠定坚实基础。在实际开发中,还需要考虑数据库集成、安全控制、异常处理等更多方面。因此,深入学习和实践Spring MVC是提升Java Web...
《Spring Web MVC初识——HelloWorld项目解析》 在IT领域,Spring框架是Java开发中的一个核心组件,尤其是它的Web MVC模块,为构建基于HTTP的Web应用提供了强大的支持。本篇文章将围绕“HelloWorld”项目,带你深入...
这个"HelloWorld"实例是初学者学习Spring MVC的入门项目,它展示了如何配置、创建和运行一个基本的Spring MVC应用。下面将详细介绍这个实例中的关键知识点。 1. **Spring MVC架构**: Spring MVC遵循模型-视图-...
在“spring-boot-helloworld”项目中,我们看到这是一个入门级的Spring Boot应用,虽然规模不大,但包含了Spring Boot的核心特性,为初学者提供了很好的学习模板。下面我们将深入探讨其中的关键知识点。 1. **...
Spring MVC 是一个强大的Java Web开发框架,用于构建可维护、高性能和灵活的Web应用程序。它作为Spring框架的一部分,提供了一种模型-视图-控制器(MVC)架构,简化了处理HTTP请求和响应的过程。本教程是针对初学者...
在IT领域,Spring MVC作为Spring框架的重要组成部分,是企业级Java应用开发中的主流MVC(Model-View-Controller)框架。本教程将通过逐步指导的方式,帮助开发者深入理解并掌握Spring MVC的核心概念和实践技巧。 一...
同时,Spring MVC还提供了异常处理、国际化、主题装饰等功能,使得Web应用开发更加灵活和强大。 总结来说,"helloworld spring mvc"项目是学习和理解Spring MVC框架的起点,它展示了Spring MVC如何处理HTTP请求,...
**Spring MVC HelloWorld Maven实例** Spring MVC是Spring框架的一部分,它是一个用于构建Web应用程序的模型-视图-控制器(MVC)架构。在这个实例中,我们将深入理解如何使用Maven构建一个基本的“Hello, World!”...
下面我们将深入探讨Spring Boot创建"Hello World"应用的关键知识点。 1. **起步依赖(Starter Dependencies)** Spring Boot的特性之一是其起步依赖,这些是Maven或Gradle的模块,可以自动配置相关的Spring功能。...
为了搭建基于Spring MVC框架和IntelliJ IDEA开发环境的Hello World项目,首先要掌握以下知识点: 1. **Spring MVC框架的理解**: - Spring MVC是Spring框架的一部分,用于构建Web应用程序。 - 它提供了一种MVC...
比如,"spring-boot-starter-web"提供了Web开发所需的所有依赖,包括Spring MVC、Tomcat等。 7. **健康检查与监控**:SpringBoot还内置了Actuator模块,提供了一组端点来监控和管理应用的健康状况,如"/health"端点...
在"SpringMVC-HelloWorld_helloworld_joint2p_"这个项目中,我们将会探索如何构建一个基础的Spring MVC应用,它能够通过网页展示出"HelloWorld"的字样。这个过程涉及到几个关键的知识点,包括Spring MVC的配置、控制...
Spring MVC 是一个基于Java的轻量级Web应用框架,它为构建Web应用程序提供了模型-视图-控制器(MVC)架构。在这个“Spring MVC Helloworld实例”中,我们将会探讨如何利用Spring MVC 3.0.5版本创建一个简单的Hello ...
在这个基于XML配置的Spring MVC HelloWorld实例中,我们将深入理解如何设置并运行一个基本的Spring MVC项目。 首先,Spring MVC的核心在于DispatcherServlet,它是整个应用的入口点,负责处理所有的HTTP请求。在`...
在 "spring-mvc-hello-world-master" 中,你可能会看到一个名为 "spring-servlet.xml" 的配置文件,它包含了 DispatcherServlet 的配置。 **6. 注解驱动开发** Spring MVC 支持注解驱动开发,这使得代码更加简洁。...