基于Spring MVC的Web应用开发(1) - HelloWorld

MVC架构

MVC是模型(model),视图(view),控制器(controller)3个单词的首字母缩写。有些应用需要处理用户请求并操纵和显示数据,MVC模式可以简化其实现。该模式由3个组件组成:

  • 模型表示用户期望看到的数据。通常情况下,模型由JavaBean组成。
  • 视图负责显示模型。文本编辑器中的视图组件会以恰当的格式显示一段文本,视图在Web应用中会生成客户端浏览器可以解释显示的HTML。
  • 控制器表示逻辑代码,负责处理请求和执行用户的意图。它会构建恰当的模型并将其传入视图进行显示。对Java Web应用来说,控制器多是一个servlet。当然,控制器可以使用任意语言实现,只要web容器支持就行。
Spring MVC介绍
Spring MVC对MVC的实现相当通用。模型就是一个包含数据的简单Map,视图就是一个interface,由它实现显示数据,控制器是Controller接口的实现。
Spring对Web应用下MVC模式的实现基于spring-webmvc下的org.springframework.web.servlet.DispatcherServlet。该Servlet会处理请求并调用恰当的控制元素对请求进行处理。Dispatcher会拦截请求并决定由哪个控制器处理请求。Spring控制器的handling方法会返回一个ModelAndView实例。该实例持有对视图和模型的引用。模型就是一个简单的Map实例,它里面是JavaBean,View接口会显示这些JavaBean。View接口定义了render方法。它遵循这样的原则:View的实现可以是任何东西,只要客户端能对其进行解析就行。
MVC实现
如果要用Spring创建Web应用,需要从基本的web.xml开始,我们在其中指定了DispatcherServlet并为其设定好相应的url-pattern。代码清单如下:
Xml代码 复制代码 收藏代码
  1. <web-appversion="2.5"xmlns="http://java.sun.com/xml/ns/javaee"
  2. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3. xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
  4. <!-- Processes application requests -->
  5. <servlet>
  6. <servlet-name>appServlet</servlet-name>
  7. <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  8. <init-param>
  9. <param-name>contextConfigLocation</param-name>
  10. <param-value>/WEB-INF/spring/appServlet/servlet-context.xml</param-value>
  11. </init-param>
  12. <load-on-startup>1</load-on-startup>
  13. </servlet>
  14. <servlet-mapping>
  15. <servlet-name>appServlet</servlet-name>
  16. <url-pattern>/</url-pattern>
  17. </servlet-mapping>
  18. <!-- Disables Servlet Container welcome file handling. Needed for compatibility with Servlet 3.0 and Tomcat 7.0 -->
  19. <welcome-file-list>
  20. <welcome-file></welcome-file>
  21. </welcome-file-list>
  22. </web-app>
<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>
上述web.xml文件定义了DispatcherServlet类作为名叫appServlet的Servlet,映射所有请求至该Servlet。
使用处理器映射
对于具体某个请求,Web应用如何知道该调用哪个servlet或者handler实现呢?Spring handler映射就在这里发挥作用了。只需要几步就可以为Spring控制器配置URL映射,因为我们使用的是Spring3.1,@Controller注解专门为此而生。
在具体展示代码前,先说明一下Spring的内部实现原理,Spring使用org.springframework.web.servlet.HandlerMappping这个接口的实现来标识调用的控制器,该接口提供了好多中实现,现在我们只要讲解hello world中需要用到的SimpleUrlHandlerMapping,凭借该handler映射,我们可以在请求中(使用全名和通配符)指定处理该请求的控制器。下列清单展示了一个handler映射的例子:
Java代码 复制代码 收藏代码
  1. package org.springframework.samples.mvc.simple;
  2. import org.springframework.stereotype.Controller;
  3. import org.springframework.web.bind.annotation.RequestMapping;
  4. import org.springframework.web.bind.annotation.ResponseBody;
  5. @Controller
  6. publicclass SimpleController {
  7. @RequestMapping("/simple")
  8. @ResponseBody
  9. public String simple() {
  10. return"Hello world!";
  11. }
  12. }
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!";
	}
	
}
Spring控制器
控制器负责处理请求:根据request构建model并将其传到view以进行显示。
DispatcherServlet有一个List类型的handlerAdapters属性,它可用来指定你想用的HandlerAdapter实现,如果这个属性值为null,就会使用默认的策略。这句话暂时不明白没关系,下文会有详细解读。
上面的代码清单其实已经继承一个控制器实现。在启动的时候我们会注意到有这样的输出日志:
Log代码 复制代码 收藏代码
  1. INFO: Mapped URL path [/simple] onto handler 'simpleController'
  2. INFO: Mapped URL path [/simple.*] onto handler 'simpleController'
  3. INFO: Mapped URL path [/simple/] onto handler 'simpleController'
INFO: Mapped URL path [/simple] onto handler 'simpleController'
INFO: Mapped URL path [/simple.*] onto handler 'simpleController'
INFO: Mapped URL path [/simple/] onto handler 'simpleController'
因为我们在web.xml中做了全局性的URL通配符,所以这里Spring“智能”的为我们的@RequestMapping("/simple")映射好三种URL(如何只保留其中一种,以后再讲)。
视图
hello world并没有视图,因为我们使用了@ResponseBody注解,该注解直接将控制器中的返回值绑定到web响应体里,如果返回的是字符串,那么该字符串就显示在浏览器上,而这也就是我们想要的。上面例子的结果是将文本hello world写入http响应流中。下面将spring官方手册中有关@ResponseBody的解释翻译一下:
翻译代码 复制代码 收藏代码
  1. @ResponseBody注解和@RequestBody相似。这个注解可以放在方法上,表明返回的类型应该是可以直接写入到HTTP响应体里的(Model中不会放置该值,该值也不会被转成一个view名字)。
@ResponseBody注解和@RequestBody相似。这个注解可以放在方法上,表明返回的类型应该是可以直接写入到HTTP响应体里的(Model中不会放置该值,该值也不会被转成一个view名字)。
视图的深入讲解将在以后逐步讲到。
至此,一个简单的hello world就讲完了,但是我们还是有疑问,这些只是表面现象,Spring底层到底是运作的呢?
首先我们来看
Xml代码 复制代码 收藏代码
  1. <servlet>
  2. <servlet-name>appServlet</servlet-name>
  3. <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  4. <init-param>
  5. <param-name>contextConfigLocation</param-name>
  6. <param-value>/WEB-INF/spring/appServlet/servlet-context.xml</param-value>
  7. </init-param>
  8. <load-on-startup>1</load-on-startup>
  9. </servlet>
<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>
系统启动时会加载DispatcherServlet这个Servlet,深入看一下:
Java代码 复制代码 收藏代码
  1. publicclass DispatcherServlet extends FrameworkServlet
public class DispatcherServlet extends FrameworkServlet
Java代码 复制代码 收藏代码
  1. publicabstractclass FrameworkServlet extends HttpServletBean
public abstract class FrameworkServlet extends HttpServletBean
Java代码 复制代码 收藏代码
  1. publicabstractclass HttpServletBean extends HttpServlet implements EnvironmentAware
public abstract class HttpServletBean extends HttpServlet implements EnvironmentAware

可见DispatcherServlet是继承了javax.servlet.http.HttpServlet的一个Servlet,既然是一个Servlet,则必然有

Java代码 复制代码 收藏代码
  1. @Override
  2. publicfinalvoid init() throws ServletException;
  3. @Override
  4. protectedfinalvoid doGet(HttpServletRequest request, HttpServletResponse response)
  5. throws ServletException, IOException;
  6. @Override
  7. protectedfinalvoid doPost(HttpServletRequest request, HttpServletResponse response)
  8. throws ServletException, IOException;
  9. @Override
  10. protectedvoid doService(HttpServletRequest request, HttpServletResponse response) throws Exception;
@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执行

Java代码 复制代码 收藏代码
  1. protectedvoid initServletBean() throws ServletException {
  2. }
protected void initServletBean() throws ServletException {
	}

讲具体的实现交给FrameworkServlt重载的方法完成

Java代码 复制代码 收藏代码
  1. @Override
  2. protectedfinalvoid initServletBean() throws ServletException {
  3. System.out.println("使用servlet上下文记录日志");
  4. getServletContext().log("Initializing Spring FrameworkServlet '" + getServletName() + "'");
  5. if (this.logger.isInfoEnabled()) {
  6. this.logger.info("FrameworkServlet '" + getServletName() + "': initialization started");
  7. }
  8. long startTime = System.currentTimeMillis();
  9. System.out.println("启动时间:[" + startTime + "]");
  10. try {
  11. System.out.println("initWebApplicationContext()");
  12. this.webApplicationContext = initWebApplicationContext();
  13. System.out.println("initFrameworkServlet(),这是一个空实现");
  14. initFrameworkServlet();
  15. }
  16. catch (ServletException ex) {
  17. this.logger.error("Context initialization failed", ex);
  18. throw ex;
  19. }
  20. catch (RuntimeException ex) {
  21. this.logger.error("Context initialization failed", ex);
  22. throw ex;
  23. }
  24. if (this.logger.isInfoEnabled()) {
  25. long elapsedTime = System.currentTimeMillis() - startTime;
  26. System.out.println("结束时间:[" + elapsedTime + "]");
  27. this.logger.info("FrameworkServlet '" + getServletName() + "': initialization completed in " +
  28. elapsedTime + " ms");
  29. }
  30. }
@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()方法,进入该方法

Java代码 复制代码 收藏代码
  1. protected WebApplicationContext initWebApplicationContext() {
  2. //System.out.println("使用WebApplicationContextUtils工具类得到WebApplicatonContext");
  3. WebApplicationContext rootContext =
  4. WebApplicationContextUtils.getWebApplicationContext(getServletContext());
  5. WebApplicationContext wac = null;
  6. //System.out.println("判断webApplicationContext是否为null");
  7. if (this.webApplicationContext != null) {
  8. //System.out.println("webApplicationContext非空!");
  9. // A context instance was injected at construction time -> use it
  10. wac = this.webApplicationContext;
  11. if (wac instanceof ConfigurableWebApplicationContext) {
  12. ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
  13. if (!cwac.isActive()) {
  14. // The context has not yet been refreshed -> provide services such as
  15. // setting the parent context, setting the application context id, etc
  16. if (cwac.getParent() == null) {
  17. // The context instance was injected without an explicit parent -> set
  18. // the root application context (if any; may be null) as the parent
  19. cwac.setParent(rootContext);
  20. }
  21. configureAndRefreshWebApplicationContext(cwac);
  22. }
  23. }
  24. }
  25. //System.out.println("判断wac是否为null");
  26. if (wac == null) {
  27. //System.out.println("was为空");
  28. // No context instance was injected at construction time -> see if one
  29. // has been registered in the servlet context. If one exists, it is assumed
  30. // that the parent context (if any) has already been set and that the
  31. // user has performed any initialization such as setting the context id
  32. wac = findWebApplicationContext();
  33. }
  34. //System.out.println("再次判断wac是否为null");
  35. if (wac == null) {
  36. System.out.println("wac还为空");
  37. System.out.println("No context instance is defined for this servlet -> create a local one");
  38. // No context instance is defined for this servlet -> create a local one
  39. wac = createWebApplicationContext(rootContext);
  40. }
  41. System.out.println("this.refreshEventReceived:[" + this.refreshEventReceived + "]");
  42. if (!this.refreshEventReceived) {
  43. System.out.println("onRefresh");
  44. // Either the context is not a ConfigurableApplicationContext with refresh
  45. // support or the context injected at construction time had already been
  46. // refreshed -> trigger initial onRefresh manually here.
  47. onRefresh(wac);
  48. }
  49. System.out.println("this.publishContext:[" + this.publishContext + "]");
  50. if (this.publishContext) {
  51. System.out.println("Publish the context as a servlet context attribute.");
  52. // Publish the context as a servlet context attribute.
  53. String attrName = getServletContextAttributeName();
  54. getServletContext().setAttribute(attrName, wac);
  55. if (this.logger.isDebugEnabled()) {
  56. this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() +
  57. "' as ServletContext attribute with name [" + attrName + "]");
  58. }
  59. }
  60. return wac;
  61. }
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);在进入这个方法

Java代码 复制代码 收藏代码
  1. protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) {
  2. System.out.println("createWebApplicationContext");
  3. Class<?> contextClass = getContextClass();
  4. if (this.logger.isDebugEnabled()) {
  5. this.logger.debug("Servlet with name '" + getServletName() +
  6. "' will try to create custom WebApplicationContext context of class '" +
  7. contextClass.getName() + "'" + ", using parent context [" + parent + "]");
  8. }
  9. if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
  10. thrownew ApplicationContextException(
  11. "Fatal initialization error in servlet with name '" + getServletName() +
  12. "': custom WebApplicationContext class [" + contextClass.getName() +
  13. "] is not of type ConfigurableWebApplicationContext");
  14. }
  15. System.out.println("初始化Bean开始");
  16. ConfigurableWebApplicationContext wac =
  17. (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
  18. System.out.println("初始化Bean结束");
  19. wac.setParent(parent);
  20. wac.setConfigLocation(getContextConfigLocation());
  21. System.out.println("configureAndRefreshWebApplicationContext开始");
  22. configureAndRefreshWebApplicationContext(wac);
  23. System.out.println("configureAndRefreshWebApplicationContext结束");
  24. return wac;
  25. }
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;
	}

于是我们就看到了有点眼熟的

Java代码 复制代码 收藏代码
  1. ConfigurableWebApplicationContext wac =
  2. (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
ConfigurableWebApplicationContext wac =
				(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);

这个方法就开始初始化我们servlet上下文即spring配置文件中的bean了。

我们的配置文件中servlet-context.xml只要一行

Xml代码 复制代码 收藏代码
  1. <beans:importresource="controllers.xml"/>
<beans:import resource="controllers.xml" />

而controllers.xml中

Xml代码 复制代码 收藏代码
  1. <context:component-scanbase-package="org.springframework.samples.mvc"/>
<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),具体过程可看如下日志:

Log代码 复制代码 收藏代码
  1. DEBUG: org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping - Looking for URL mappings in application context: WebApplicationContext for namespace 'appServlet-servlet': startup date [Fri Mar 1605:36:03 CST 2012]; root of context hierarchy
  2. DEBUG: org.springframework.beans.factory.support.DefaultListableBeanFactory - Returning cached instance of singleton bean 'simpleController'
  3. 将一个URL路径映射到一个handler上
  4. INFO : org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping - Mapped URL path [/simple] onto handler 'simpleController'
  5. DEBUG: org.springframework.beans.factory.support.DefaultListableBeanFactory - Returning cached instance of singleton bean 'simpleController'
  6. 将一个URL路径映射到一个handler上
  7. INFO : org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping - Mapped URL path [/simple.*] onto handler 'simpleController'
  8. DEBUG: org.springframework.beans.factory.support.DefaultListableBeanFactory - Returning cached instance of singleton bean 'simpleController'
  9. 将一个URL路径映射到一个handler上
  10. INFO : org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping - Mapped URL path [/simple/] onto handler 'simpleController'
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的这个方法中

Java代码 复制代码 收藏代码
  1. /**
  2. * Initialize the HandlerAdapters used by this class.
  3. * <p>If no HandlerAdapter beans are defined in the BeanFactory for this namespace,
  4. * we default to SimpleControllerHandlerAdapter.
  5. */
  6. privatevoid initHandlerAdapters(ApplicationContext context) {
  7. this.handlerAdapters = null;
  8. if (this.detectAllHandlerAdapters) {
  9. // Find all HandlerAdapters in the ApplicationContext, including ancestor contexts.
  10. Map<String, HandlerAdapter> matchingBeans =
  11. BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerAdapter.class, true, false);
  12. if (!matchingBeans.isEmpty()) {
  13. this.handlerAdapters = new ArrayList<HandlerAdapter>(matchingBeans.values());
  14. // We keep HandlerAdapters in sorted order.
  15. OrderComparator.sort(this.handlerAdapters);
  16. }
  17. }
  18. else {
  19. try {
  20. HandlerAdapter ha = context.getBean(HANDLER_ADAPTER_BEAN_NAME, HandlerAdapter.class);
  21. this.handlerAdapters = Collections.singletonList(ha);
  22. }
  23. catch (NoSuchBeanDefinitionException ex) {
  24. // Ignore, we'll add a default HandlerAdapter later.
  25. }
  26. }
  27. // Ensure we have at least some HandlerAdapters, by registering
  28. // default HandlerAdapters if no other adapters are found.
  29. if (this.handlerAdapters == null) {
  30. System.out.println("确保我们有一些handlerAdapters");
  31. this.handlerAdapters = getDefaultStrategies(context, HandlerAdapter.class);
  32. if (logger.isDebugEnabled()) {
  33. logger.debug("No HandlerAdapters found in servlet '" + getServletName() + "': using default");
  34. }
  35. }
  36. }
/**
	 * 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,具体日志:

Java代码 复制代码 收藏代码
  1. DEBUG: org.springframework.beans.factory.support.DefaultListableBeanFactory - Finished creating instance of bean 'org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter'
  2. DEBUG: org.springframework.beans.factory.support.DefaultListableBeanFactory - Finished creating instance of bean 'org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter'
  3. DEBUG: org.springframework.beans.factory.support.DefaultListableBeanFactory - Finished creating instance of bean 'org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter'
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,也就是项目就运行起来了。

下面讲解请求部分

先来看一段浏览器发出请求后,服务端的记录的日志

Log代码 复制代码 收藏代码
  1. FrameworkServlet doGet
  2. TRACE: org.springframework.web.servlet.DispatcherServlet - Bound request context to thread: [GET /web/simple]@902324502 org.eclipse.jetty.server.Request@35c86116
  3. doService
  4. DEBUG: org.springframework.web.servlet.DispatcherServlet - DispatcherServlet with name 'appServlet' processing GET request for [/web/simple]
  5. doDispatch
  6. Determine handler for the current request.
  7. TRACE: org.springframework.web.servlet.DispatcherServlet - Testing handler map [org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping@20d1fa4] in DispatcherServlet with name 'appServlet'
  8. TRACE: org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping - No handler mapping found for [/simple]
  9. TRACE: org.springframework.web.servlet.DispatcherServlet - Testing handler map [org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping@5b33a7af] in DispatcherServlet with name 'appServlet'
  10. DEBUG: org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping - Mapping [/simple] to HandlerExecutionChain with handler [org.springframework.samples.mvc.simple.SimpleController@573b7064] and 1 interceptor
  11. Determine handler adapter for the current request.
  12. TRACE: org.springframework.web.servlet.DispatcherServlet - Testing handler adapter [org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter@557ce3bb]
  13. TRACE: org.springframework.web.servlet.DispatcherServlet - Testing handler adapter [org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter@6996a298]
  14. TRACE: org.springframework.web.servlet.DispatcherServlet - Testing handler adapter [org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter@783b67a7]
  15. Process last-modified header, if supported by the handler.
  16. DEBUG: org.springframework.web.servlet.DispatcherServlet - Last-Modified value for [/web/simple] is: -1
  17. Apply preHandle methods of registered interceptors.
  18. Actually invoke the handler.
  19. class org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter
  20. invokeHandlerMethod
  21. 真正处理
  22. DEBUG: org.springframework.web.bind.annotation.support.HandlerMethodInvoker - Invoking request handler method: public java.lang.String org.springframework.samples.mvc.simple.SimpleController.simple()
  23. result:[Hello world!]
  24. getModelAndView[class java.lang.String]
  25. 原来进入了这个分支
  26. OK搞定
  27. 具体输出数据
  28. DEBUG: org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter - Written [Hello world!] as "text/html" using [org.springframework.http.converter.StringHttpMessageConverter@6b7de5ce]
  29. 返回mv为空,假设handler已经处理完了
  30. Do we need view name translation?
  31. Apply postHandle methods of registered interceptors.
  32. DEBUG: org.springframework.web.servlet.DispatcherServlet - Null ModelAndView returned to DispatcherServlet with name 'appServlet': assuming HandlerAdapter completed request handling
  33. Trigger after-completion for successful outcome.
  34. [1]
  35. TRACE: org.springframework.web.servlet.DispatcherServlet - Cleared thread-bound request context: [GET /web/simple]@902324502 org.eclipse.jetty.server.Request@35c86116
  36. DEBUG: org.springframework.web.servlet.DispatcherServlet - Successfully completed request
  37. 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
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方法,然后调用

Java代码 复制代码 收藏代码
  1. /**
  2. * Process this request, publishing an event regardless of the outcome.
  3. * <p>The actual event handling is performed by the abstract
  4. * {@link #doService} template method.
  5. */
  6. protectedfinalvoid processRequest(HttpServletRequest request, HttpServletResponse response)
  7. throws ServletException, IOException {
  8. long startTime = System.currentTimeMillis();
  9. Throwable failureCause = null;
  10. // Expose current LocaleResolver and request as LocaleContext.
  11. LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
  12. LocaleContextHolder.setLocaleContext(buildLocaleContext(request), this.threadContextInheritable);
  13. // Expose current RequestAttributes to current thread.
  14. RequestAttributes previousRequestAttributes = RequestContextHolder.getRequestAttributes();
  15. ServletRequestAttributes requestAttributes = null;
  16. if (previousRequestAttributes == null || previousRequestAttributes.getClass().equals(ServletRequestAttributes.class)) {
  17. requestAttributes = new ServletRequestAttributes(request);
  18. RequestContextHolder.setRequestAttributes(requestAttributes, this.threadContextInheritable);
  19. }
  20. if (logger.isTraceEnabled()) {
  21. logger.trace("Bound request context to thread: " + request);
  22. }
  23. try {
  24. doService(request, response);
  25. }
  26. catch (ServletException ex) {
  27. failureCause = ex;
  28. throw ex;
  29. }
  30. catch (IOException ex) {
  31. failureCause = ex;
  32. throw ex;
  33. }
  34. catch (Throwable ex) {
  35. failureCause = ex;
  36. thrownew NestedServletException("Request processing failed", ex);
  37. }
  38. finally {
  39. // Clear request attributes and reset thread-bound context.
  40. LocaleContextHolder.setLocaleContext(previousLocaleContext, this.threadContextInheritable);
  41. if (requestAttributes != null) {
  42. RequestContextHolder.setRequestAttributes(previousRequestAttributes, this.threadContextInheritable);
  43. requestAttributes.requestCompleted();
  44. }
  45. if (logger.isTraceEnabled()) {
  46. logger.trace("Cleared thread-bound request context: " + request);
  47. }
  48. if (logger.isDebugEnabled()) {
  49. if (failureCause != null) {
  50. this.logger.debug("Could not complete request", failureCause);
  51. }
  52. else {
  53. this.logger.debug("Successfully completed request");
  54. }
  55. }
  56. if (this.publishEvents) {
  57. // Whether or not we succeeded, publish an event.
  58. long processingTime = System.currentTimeMillis() - startTime;
  59. this.webApplicationContext.publishEvent(
  60. new ServletRequestHandledEvent(this,
  61. request.getRequestURI(), request.getRemoteAddr(),
  62. request.getMethod(), getServletConfig().getServletName(),
  63. WebUtils.getSessionId(request), getUsernameForRequest(request),
  64. processingTime, failureCause));
  65. }
  66. }
  67. }
/**
	 * 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重写

Java代码 复制代码 收藏代码
  1. @Override
  2. protectedvoid doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
  3. System.out.println("doService");
  4. if (logger.isDebugEnabled()) {
  5. String requestUri = urlPathHelper.getRequestUri(request);
  6. logger.debug("DispatcherServlet with name '" + getServletName() + "' processing " + request.getMethod() +
  7. " request for [" + requestUri + "]");
  8. }
  9. // Keep a snapshot of the request attributes in case of an include,
  10. // to be able to restore the original attributes after the include.
  11. Map<String, Object> attributesSnapshot = null;
  12. if (WebUtils.isIncludeRequest(request)) {
  13. logger.debug("Taking snapshot of request attributes before include");
  14. attributesSnapshot = new HashMap<String, Object>();
  15. Enumeration<?> attrNames = request.getAttributeNames();
  16. while (attrNames.hasMoreElements()) {
  17. String attrName = (String) attrNames.nextElement();
  18. if (this.cleanupAfterInclude || attrName.startsWith("org.springframework.web.servlet")) {
  19. attributesSnapshot.put(attrName, request.getAttribute(attrName));
  20. }
  21. }
  22. }
  23. this.flashMapManager.requestStarted(request);
  24. // Make framework objects available to handlers and view objects.
  25. request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
  26. request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
  27. request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
  28. request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());
  29. try {
  30. doDispatch(request, response);
  31. }
  32. finally {
  33. this.flashMapManager.requestCompleted(request);
  34. // Restore the original attribute snapshot, in case of an include.
  35. if (attributesSnapshot != null) {
  36. restoreAttributesAfterInclude(request, attributesSnapshot);
  37. }
  38. }
  39. }
@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方法

Java代码 复制代码 收藏代码
  1. protectedvoid doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
  2. System.out.println("doDispatch");
  3. HttpServletRequest processedRequest = request;
  4. HandlerExecutionChain mappedHandler = null;
  5. int interceptorIndex = -1;
  6. try {
  7. ModelAndView mv;
  8. boolean errorView = false;
  9. try {
  10. processedRequest = checkMultipart(request);
  11. // Determine handler for the current request.
  12. System.out.println("Determine handler for the current request.");
  13. mappedHandler = getHandler(processedRequest, false);
  14. if (mappedHandler == null || mappedHandler.getHandler() == null) {
  15. noHandlerFound(processedRequest, response);
  16. return;
  17. }
  18. // Determine handler adapter for the current request.
  19. System.out.println("Determine handler adapter for the current request.");
  20. HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
  21. // Process last-modified header, if supported by the handler.
  22. System.out.println("Process last-modified header, if supported by the handler.");
  23. String method = request.getMethod();
  24. boolean isGet = "GET".equals(method);
  25. if (isGet || "HEAD".equals(method)) {
  26. long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
  27. if (logger.isDebugEnabled()) {
  28. String requestUri = urlPathHelper.getRequestUri(request);
  29. logger.debug("Last-Modified value for [" + requestUri + "] is: " + lastModified);
  30. }
  31. if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
  32. return;
  33. }
  34. }
  35. // Apply preHandle methods of registered interceptors.
  36. System.out.println("Apply preHandle methods of registered interceptors.");
  37. HandlerInterceptor[] interceptors = mappedHandler.getInterceptors();
  38. if (interceptors != null) {
  39. for (int i = 0; i < interceptors.length; i++) {
  40. HandlerInterceptor interceptor = interceptors[i];
  41. if (!interceptor.preHandle(processedRequest, response, mappedHandler.getHandler())) {
  42. triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null);
  43. return;
  44. }
  45. interceptorIndex = i;
  46. }
  47. }
  48. // Actually invoke the handler.
  49. System.out.println("Actually invoke the handler.");
  50. mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
  51. System.out.println("返回mv为空,假设handler已经处理完了");
  52. // Do we need view name translation?
  53. System.out.println("Do we need view name translation?");
  54. if (mv != null && !mv.hasView()) {
  55. mv.setViewName(getDefaultViewName(request));
  56. }
  57. // Apply postHandle methods of registered interceptors.
  58. System.out.println("Apply postHandle methods of registered interceptors.");
  59. if (interceptors != null) {
  60. for (int i = interceptors.length - 1; i >= 0; i--) {
  61. HandlerInterceptor interceptor = interceptors[i];
  62. interceptor.postHandle(processedRequest, response, mappedHandler.getHandler(), mv);
  63. }
  64. }
  65. }
  66. catch (ModelAndViewDefiningException ex) {
  67. logger.debug("ModelAndViewDefiningException encountered", ex);
  68. mv = ex.getModelAndView();
  69. }
  70. catch (Exception ex) {
  71. Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
  72. mv = processHandlerException(processedRequest, response, handler, ex);
  73. errorView = (mv != null);
  74. }
  75. // Did the handler return a view to render?
  76. if (mv != null && !mv.wasCleared()) {
  77. System.out.println("执行render了");
  78. render(mv, processedRequest, response);
  79. if (errorView) {
  80. WebUtils.clearErrorRequestAttributes(request);
  81. }
  82. }
  83. else {
  84. if (logger.isDebugEnabled()) {
  85. logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() +
  86. "': assuming HandlerAdapter completed request handling");
  87. }
  88. }
  89. // Trigger after-completion for successful outcome.
  90. System.out.println("Trigger after-completion for successful outcome.");
  91. triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null);
  92. }
  93. catch (Exception ex) {
  94. // Trigger after-completion for thrown exception.
  95. triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, ex);
  96. throw ex;
  97. }
  98. catch (Error err) {
  99. ServletException ex = new NestedServletException("Handler processing failed", err);
  100. // Trigger after-completion for thrown exception.
  101. triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, ex);
  102. throw ex;
  103. }
  104. finally {
  105. // Clean up any resources used by a multipart request.
  106. if (processedRequest != request) {
  107. cleanupMultipart(processedRequest);
  108. }
  109. }
  110. }
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);
			}
		}
	}

这里才是最精髓的,代码

Java代码 复制代码 收藏代码
  1. System.out.println("Determine handler for the current request.");
System.out.println("Determine handler for the current request.");

对应日志部分

Log代码 复制代码 收藏代码
  1. Determine handler for the current request.
  2. TRACE: org.springframework.web.servlet.DispatcherServlet - Testing handler map [org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping@20d1fa4] in DispatcherServlet with name 'appServlet'
  3. TRACE: org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping - No handler mapping found for [/simple]
  4. TRACE: org.springframework.web.servlet.DispatcherServlet - Testing handler map [org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping@5b33a7af] in DispatcherServlet with name 'appServlet'
  5. 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 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

代码

Java代码 复制代码 收藏代码
  1. Determine handler adapter for the current request.
Determine handler adapter for the current request.

对应日志部分

Log代码 复制代码 收藏代码
  1. Determine handler adapter for the current request.
  2. TRACE: org.springframework.web.servlet.DispatcherServlet - Testing handler adapter [org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter@557ce3bb]
  3. TRACE: org.springframework.web.servlet.DispatcherServlet - Testing handler adapter [org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter@6996a298]
  4. TRACE: org.springframework.web.servlet.DispatcherServlet - Testing handler adapter [org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter@783b67a7]
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,有对应关系,搞定。

下面来到这段代码

Java代码 复制代码 收藏代码
  1. System.out.println("Actually invoke the handler.");
  2. mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
  3. System.out.println("返回mv为空,假设handler已经处理完了");
System.out.println("Actually invoke the handler.");
				mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
System.out.println("返回mv为空,假设handler已经处理完了");

注意这里的mappedHandler.geteHandler()就是一个AnnotationMethodHandlerAdapter。

再调用

Java代码 复制代码 收藏代码
  1. public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
  2. throws Exception {
  3. System.out.println(getClass());
  4. Class<?> clazz = ClassUtils.getUserClass(handler);
  5. Boolean annotatedWithSessionAttributes = this.sessionAnnotatedClassesCache.get(clazz);
  6. if (annotatedWithSessionAttributes == null) {
  7. annotatedWithSessionAttributes = (AnnotationUtils.findAnnotation(clazz, SessionAttributes.class) != null);
  8. this.sessionAnnotatedClassesCache.put(clazz, annotatedWithSessionAttributes);
  9. }
  10. if (annotatedWithSessionAttributes) {
  11. // Always prevent caching in case of session attribute management.
  12. checkAndPrepare(request, response, this.cacheSecondsForSessionAttributeHandlers, true);
  13. // Prepare cached set of session attributes names.
  14. }
  15. else {
  16. // Uses configured default cacheSeconds setting.
  17. checkAndPrepare(request, response, true);
  18. }
  19. // Execute invokeHandlerMethod in synchronized block if required.
  20. if (this.synchronizeOnSession) {
  21. System.out.println("同步");
  22. HttpSession session = request.getSession(false);
  23. if (session != null) {
  24. Object mutex = WebUtils.getSessionMutex(session);
  25. synchronized (mutex) {
  26. return invokeHandlerMethod(request, response, handler);
  27. }
  28. }
  29. }
  30. return invokeHandlerMethod(request, response, handler);
  31. }
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方法

Java代码 复制代码 收藏代码
  1. protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, Object handler)
  2. throws Exception {
  3. System.out.println("invokeHandlerMethod");
  4. ServletHandlerMethodResolver methodResolver = getMethodResolver(handler);
  5. Method handlerMethod = methodResolver.resolveHandlerMethod(request);
  6. ServletHandlerMethodInvoker methodInvoker = new ServletHandlerMethodInvoker(methodResolver);
  7. ServletWebRequest webRequest = new ServletWebRequest(request, response);
  8. ExtendedModelMap implicitModel = new BindingAwareModelMap();
  9. Object result = methodInvoker.invokeHandlerMethod(handlerMethod, handler, webRequest, implicitModel);
  10. System.out.println("result:[" + result + "]");
  11. ModelAndView mav =
  12. methodInvoker.getModelAndView(handlerMethod, handler.getClass(), result, implicitModel, webRequest);
  13. methodInvoker.updateModelAttributes(handler, (mav != null ? mav.getModel() : null), implicitModel, webRequest);
  14. return mav;
  15. }
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方法

Java代码 复制代码 收藏代码
  1. public ModelAndView getModelAndView(Method handlerMethod, Class handlerType, Object returnValue,
  2. ExtendedModelMap implicitModel, ServletWebRequest webRequest) throws Exception {
  3. System.out.println("getModelAndView[" + returnValue.getClass() + "]");
  4. ResponseStatus responseStatusAnn = AnnotationUtils.findAnnotation(handlerMethod, ResponseStatus.class);
  5. if (responseStatusAnn != null) {
  6. HttpStatus responseStatus = responseStatusAnn.value();
  7. String reason = responseStatusAnn.reason();
  8. if (!StringUtils.hasText(reason)) {
  9. webRequest.getResponse().setStatus(responseStatus.value());
  10. }
  11. else {
  12. webRequest.getResponse().sendError(responseStatus.value(), reason);
  13. }
  14. // to be picked up by the RedirectView
  15. webRequest.getRequest().setAttribute(View.RESPONSE_STATUS_ATTRIBUTE, responseStatus);
  16. responseArgumentUsed = true;
  17. }
  18. // Invoke custom resolvers if present...
  19. if (customModelAndViewResolvers != null) {
  20. System.out.println("Invoke custom resolvers if present...");
  21. for (ModelAndViewResolver mavResolver : customModelAndViewResolvers) {
  22. ModelAndView mav = mavResolver.resolveModelAndView(
  23. handlerMethod, handlerType, returnValue, implicitModel, webRequest);
  24. if (mav != ModelAndViewResolver.UNRESOLVED) {
  25. return mav;
  26. }
  27. }
  28. }
  29. if (returnValue instanceof HttpEntity) {
  30. handleHttpEntityResponse((HttpEntity<?>) returnValue, webRequest);
  31. returnnull;
  32. }
  33. elseif (AnnotationUtils.findAnnotation(handlerMethod, ResponseBody.class) != null) {
  34. System.out.println("原来进入了这个分支");
  35. handleResponseBody(returnValue, webRequest);
  36. returnnull;
  37. }
  38. elseif (returnValue instanceof ModelAndView) {
  39. ModelAndView mav = (ModelAndView) returnValue;
  40. mav.getModelMap().mergeAttributes(implicitModel);
  41. return mav;
  42. }
  43. elseif (returnValue instanceof Model) {
  44. returnnew ModelAndView().addAllObjects(implicitModel).addAllObjects(((Model) returnValue).asMap());
  45. }
  46. elseif (returnValue instanceof View) {
  47. returnnew ModelAndView((View) returnValue).addAllObjects(implicitModel);
  48. }
  49. elseif (AnnotationUtils.findAnnotation(handlerMethod, ModelAttribute.class) != null) {
  50. addReturnValueAsModelAttribute(handlerMethod, handlerType, returnValue, implicitModel);
  51. returnnew ModelAndView().addAllObjects(implicitModel);
  52. }
  53. elseif (returnValue instanceof Map) {
  54. returnnew ModelAndView().addAllObjects(implicitModel).addAllObjects((Map) returnValue);
  55. }
  56. elseif (returnValue instanceof String) {
  57. System.out.println("返回值为String");
  58. returnnew ModelAndView((String) returnValue).addAllObjects(implicitModel);
  59. }
  60. elseif (returnValue == null) {
  61. // Either returned null or was 'void' return.
  62. if (this.responseArgumentUsed || webRequest.isNotModified()) {
  63. returnnull;
  64. }
  65. else {
  66. // Assuming view name translation...
  67. returnnew ModelAndView().addAllObjects(implicitModel);
  68. }
  69. }
  70. elseif (!BeanUtils.isSimpleProperty(returnValue.getClass())) {
  71. // Assume a single model attribute...
  72. addReturnValueAsModelAttribute(handlerMethod, handlerType, returnValue, implicitModel);
  73. returnnew ModelAndView().addAllObjects(implicitModel);
  74. }
  75. else {
  76. thrownew IllegalArgumentException("Invalid handler method return value: " + returnValue);
  77. }
  78. }
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方法

Java代码 复制代码 收藏代码
  1. privatevoid handleResponseBody(Object returnValue, ServletWebRequest webRequest)
  2. throws Exception {
  3. System.out.println("OK搞定");
  4. if (returnValue == null) {
  5. return;
  6. }
  7. HttpInputMessage inputMessage = createHttpInputMessage(webRequest);
  8. HttpOutputMessage outputMessage = createHttpOutputMessage(webRequest);
  9. writeWithMessageConverters(returnValue, inputMessage, outputMessage);
  10. }
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中。

Shell代码 复制代码 收藏代码
  1. git clone git://github.com/stephansun/samples.git
git clone git://github.com/stephansun/samples.git

最后补充一下pom.xml

Xml代码 复制代码 收藏代码
  1. <dependencies>
  2. <dependency>
  3. <groupId>org.springframework</groupId>
  4. <artifactId>spring-webmvc</artifactId>
  5. <version>3.1.0.RELEASE</version>
  6. </dependency>
  7. </dependencies>
<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的代码

猜你喜欢

转载自zxb1985.iteye.com/blog/1813950
今日推荐