例子来自spring-framework-2.5.6.SEC01\docs\MVC-step-by-step\pdf\spring-mvc-step-by-step.pdf。
先上图,再慢慢解释。
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-name>-servlet.xml,所以本案例的WebApplicationContext文件即是springapp-servlet.xml。
bean name必须是slash开头,然后只能写request中最后一个slash后面的部分。
<!-- spingapp/war/WEB-INF/springapp-servlet.xml --> <bean name="/hello.htm" class="springapp.web.InventoryController"> <property ......></property> </bean>
另外还有SimpleUrlHandlerMapping、ControllerClassNameHandlerMapping和CommonsPathMapHandlerMapping三种mapping形式,具体参见Spring in Action 2nd Edition 13.2
3. Controller返回ModelAndView给DispatchServlet
Controller的类型有很多,最简单的形式就是自己实现一个Controller接口(spring自带了很多Controller及其子接口的实现),只需实现一个handleRequest(HttpServletRequest, HttpServletResponse)方法,返回一个ModelAndView即可。
ModelAndView(ViewName, ModelName, ModelObject),其中的ModelName和ModelObject相当于给ViewName setAttribute(ModelName, ModelObject);ViewName可以写一个长路径,如“WEB-INF/jsp/hello.jsp”,更常见的方法是返回一个短字符串交给ViewResolver去解析。
4. DispatchServlet通过ViewResolver来解析ViewName
DispatchServlet默认使用的ViewResolver是InternalResourceViewResolver(更多关于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>