Spring MVC example anatomy

Yao Yao on November 29, 2009

例子来自 Developing a Spring Framework MVC application step-by-step ,版本是 spring-framework-2.5.6.SEC01

1. DispatchServlet 接过浏览器的 /hello.htm请求

在 springapp/war/WEB-INF/web.xml 中,定义了 homepage:

<welcome-file-list>
	<welcome-file>index.jsp</welcome-file>
</welcome-file-list>

所以 http://localhost:8080/springapp 即相当于 http://localhost:8080/springapp/index.jsp。

而 springapp/war/index.jsp 是直接 sendRedirect:

<%-- Redirected because we can't set the welcome page to a virtual URL. --%>
<c:redirect url="/hello.htm"/>

所以又转到 http://localhost:8080/springapp/hello.htm

在 springapp/war/WEB-INF/web.xml 中有:

<servlet>  
	<servlet-name>springapp</servlet-name>  
	<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>  
</servlet>  
  
<servlet-mapping>  
	<servlet-name>springapp</servlet-name>  
	<url-pattern>*.htm</url-pattern>  
</servlet-mapping>

所以这个 http://localhost:8080/springapp/hello.htm 请求由 DispatcherServlet 来处理。

2. DispatchServlet 确定 mapping of <Request, Controller>

DispatchServlet 默认使用的是 BeanNameUrlHandlerMapping,即通过 controller 的 bean name 来与 request 对应。

Controller 在 WebApplicationContext 文件中定义,而 WebApplicationContext 文件的命名规则是 -servlet.xml,所以本案例的 WebApplicationContext 文件即是 springapp-servlet.xml。

bean name 必须是 slash 开头,然后只能写 request 中最后一个 slash 后面的部分。

<bean name="/hello.htm" class="springapp.web.InventoryController">  
	<property xxx="zzz"></property>  
</bean> 

另外还有 SimpleUrlHandlerMapping、ControllerClassNameHandlerMapping 和 CommonsPathMapHandlerMapping 三种 mapping 形式,这里就不展开了。

3. Controller 返回 ModelAndView 给 DispatchServlet

Controller 的类型有很多,最简单的形式就是自己实现一个 Controller 接口 (spring 自带了很多 Controller 及其子接口的实现),只需实现一个 handleRequest(HttpServletRequest, HttpServletResponse) 方法,返回一个 ModelAndView 即可。

ModelAndView(viewName, modelName, modelObject),其中的 modelNamemodelObject 是一个 pair;viewName 可以写一个长路径,如 “WEB-INF/jsp/hello.jsp”,更常见的方法是返回一个短字符串,比如 “hello”,交给 ViewResolver 去解析。

4. DispatchServlet 通过 ViewResolver 来解析 ViewName

DispatchServlet 默认使用的 ViewResolverInternalResourceViewResolver (更多关于 DispatchServlet 的默认配置请参见 spring-framework-2.5.6.SEC01\src\org\springframework\web\servlet\DispatcherServlet.properties),但与 BeanNameUrlHandlerMapping 不同的是,虽然 InternalResourceViewResolver 是默认的,但需要进一步对 InternalResourceViewResolver 进行配置。

<!-- spingapp/war/WEB-INF/springapp-servlet.xml -->  
  
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">  
	<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"></property>  
	<property name="prefix" value="/WEB-INF/jsp/"></property>  
	<property name="suffix" value=".jsp"></property>  
</bean>  

prefix + “hello” + suffix == “/WEB-INF/jsp/hello.jsp”

注意这里的 bean id,虽然在 spring-framework-2.5.6.SEC01\src\org\springframework\web\servlet\DispatcherServlet.java 里有:

public static final String VIEW_RESOLVER_BEAN_NAME = "viewResolver";  

但这里的 bean id 可以随便写。

However, MessageSource 类的 bean id 必须是 “messageSource”,不能随便写。我们在 spring-framework-2.5.6.SEC01\src\org\springframework\context\support\AbstractApplicationContext.java 里面可以看到有:

public static final String MESSAGE_SOURCE_BEAN_NAME = "messageSource";  

另外,bean name 和 bean id 的区别:

Either one would work. It depends on your needs: If your bean identifier contains special character(s) for example (/viewSummary.html), it (the slash) won’t be allowed as the bean id, because it’s not a valid XML ID. In such cases you could skip defining the bean id and supply the bean name instead. The name attribute also helps in defining aliases for your bean, since it allows specifying multiple identifiers for a given bean.

5. JSP dispatched to browser by DispatchServlet

hello.jsp 被 dispatch 给浏览器显示,注意是 dispatch,所以浏览器的地址栏仍然是 hello.htm。

JSP 可以使用 ModelAndView 中的 Model,形式如 “${ModelName.ModelObject}”,类似于 getAttribute。

<%-- springapp/war/WEB-INF/jsp/hello.jsp --%>  
  
<body>  
	<h1><fmt:message key="heading"/></h1>  
	<p><fmt:message key="greeting"/> <c:out value="${model.now}"></c:out></p>  
	<h3>Product</h3>  
	<c:forEach items="${model.products}" var="prod">  
		<c:out value="${prod.description}"/>   
		<i><c:out value="${prod.price}"/></i><br><br>  
	</c:forEach>  
	<br>  
	<a href="<c:url value="priceincrease.htm"/>">Increase Price</a>  
	<br>  
</body>  


blog comments powered by Disqus