第23章 Spring MVC初体验
实践出真知
如果你已经读到了这里,我完全能够理解你要实现第一个基于SpringMVC的Web应用程序的迫切心情,毕竟,一味地纸上谈兵令人腻烦,那么让我们开始一个我称之为SimpleFx
的Web应用程序的开发吧!
一个外汇交易系统与其他的软件系统在本质上没有任何差别,为了能够便于一些数据的管理,我们也需要一个用于后台管理的应用程序。既然是讲解SpringMVC,我们打算以Web应用程序的形式来开发后台管理功能,这个用于后台管理的Web应用程序我们暂且就称其为 SimpleFX 。因为不打算完全实现它所有的功能,只要能够通过几个功能的开发阐明SpringMVC的相应特点即可。
整个后台管理程序的显示布局如图23-4所示。
其中,左边的各个FunctionSet
表示相应的管理功能集合,我们可以通过点击这些FunctionSet
的链接来触发相应的管理请求,剩下的部分为主画面显示,一看便知。
本次我们要开发的是一个简单的显示外汇交易当天评价汇率的Web请求处理流程。在点击相应的链接之后,我们可以获取当天评价汇率的显示信息,类似于图23-5所示的主画面显示。
Spring MVC应用的物理结构
虽然我们已经从总体上了解了SpringMVC框架在处理Web请求过程中所涉及的几个主要成员,但目前为止,它们只是逻辑上的概念,对于我们来说,只是一座座的空中楼阁。我们现在得面对现实,从实体结构上看一下,一个典型的基于SpringMVC的Web应用程序,通常是如何组装而成的(听起来好像我们在造一辆汽车的感觉),然后再真正地开始着手构建我们的第一个基于SpringMVC的Web应用程序。
一个基于SpringMVC框架的Web应用,依然是一个遵循Servlet规范的Web应用程序,自然,它也就拥有一个遵循Servlet规范的Web应用程序应有的目录结构和相应的部署描述符(Deployment Descriptor)文件。只不过,在此基础上,基于SpringMVC框架的Web应用会再增加两个(或者多个)配置文件,整个图景如图23-6所示。
我们以web.xml
作为出发点,它是整个Web应用程序的部署描述符文件,这是所有基于Servlet规范的Web应用程序都有的东西,我想大家对它应该并不陌生。在图23-6右边的就是web.xml
的具体内容。
当然,在这个部署描述符文件中,我们还可以根据需要添加其他元素,比如可能需要进行字符编码的转换,那会增加一个Filter的定义,或者添加welcome文件列表以及异常处理页面相关内容等。不过为了避免混淆视听,我们这里仅挑了与SpringMVC有关的元素进行重点阐述。
1.ContextLoaderListener与WEB-INF/applicationContext.xml
从web.xml
开始,我们首先通过<listener>
元素增加了一个ServletContextListener
的定义,即org.springframework.Web.context.ContextLoaderListener
。
ContextLoaderListener
的职责在于,它将为整个的Web应用程序加载顶层的WebApplicationContext
(ROOT
WebApplicationContext)。该顶层WebApplicationContext主要用于 提供应用所使用的中间层服务 。
我们所使用的数据源(DataSource)定义、数据访问对象(DAO)定义、服务对象(Services)定义等,都在该WebApplicationContext
中注册。你完全可以将其比作独立运行的应用程序中我们所使用的ClassPathXmlApplicationContext
或者FileSystemXmlApp1icationContext
。
只不过,WebApplicationContext
专门用于Web环境下,
在这种容器中,我们可以使用在讲解Spring的IoC容器部分提到的自定义scope来注册相应的bean定义了,包括request、session等。
ContextLoaderListener
加载的webApplicationContext
的默认配置文件路径为WEB- INF/applicationContext.xml
,这也就是图23-6中的/WEB- INF/
目录下存在一个applicationContext.xml
配置文件的原因。该文件符合通用的Spring IoC容器配置文件格式。
实际开发中,不管是出于团队并行开发效率的考虑,还是出于便于管理的因素考虑,很少会使用默认的单一的/WEB- INF/applicationContext.xml
来管理整个Web应用程序的所有中间层服务对象。我们要么会按照应用程序的层次进行配置文件的分割,要么会按照系统功能模块进行配置文件的分割。当存在多个分割后的配置文件的时候,ContextLoaderListener
的默认加载行为将成为我们的制约。这时,我们可以通过在web.xml
中指定名称为contextConfigLocation
的配置参数来打破默认行为的制约,如下方代码所示。
<context-paran>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-TNF/applicationContext.xml,/WEB-INF/applicationContextmodule1.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.Web.context.ContextLoaderListener
</listener-class>
</1istener>
我们可以在<param- value>
中通过逗号或者空格来分割多个配置文件路径,甚至使用ANT类型的路径表达式。更多信息可以参阅org.springframework.web.context.ContextLoader
类的Javadoc文档,因为ContextLoaderListener
最终委派该类进行WebApplicationContext
的加载。
ContextLoaderListener
或者ContextLoaderServlet
加载相应路径下的容器配置文件,并在构建完成相应的顶层WebApplicationContext
后,将该顶层WebApplicationContext
绑定到ServletContext
。
如果想获取绑定到ServletContext
的WebApplicationContext
,我们可以通过org.springframework.web.context.support.WebApplicationContextUtils
类。这样就不需要知道顶层WebApplicationContext
绑定到ServletContext
的时候使用的Key是什么,例如:
WebApplicationContext wac = WebApplicationContextUtils.getWebApplicationContext(getServletContext());
或者
WebApplicationContext wac = WebApplicationContextUtils.getRequiredApplicationContext(getServletContext());
wac.getBean("serviceBeanName");
了解这一点的主要目的在于,绑定到ServletContext
的顶层webApplicationContext
并非只能在基于SpringMVC的Web应用程序中使用。通过ContextLoaderListener
或者ContextLoaderServlet
将其绑定到ServletContext
,任何类型的Web应用程序(当然指的是基于Java平台技术的Web应用程序),只要能够获取ServletContext
的引用,就能获取并使用该WebApplicationContext
。
换句话说就是,即使当前使用的Web开发框架没有类似于Spring提供的基于IoC容器的中间层业务对象管理支持,通过在web.xml
中注册ContextLoaderListener
或者ContextLoaderServlet
,也可以获取这种支持。所谓的集成Spring与第三方Web应用框架,最多只是在此基础上做进一步的扩展。
2. DispatcherServlet与XXX-servlet.xml
对于web.xml
中org.springframework.Web.servlet.DispatcherServlet
的注册,表面看起来并无太多玄机,与注册其他Servlet并无二致,但事实并非如此。
我们说过,DispatcherServlet
是基于SpringMVC框架Web应用程序的FrontController
,它将负责几乎所有对应当前Web应用程序的Web请求的处理。DispatcherServlet
使用了一个外部化的配置文件,
用来配置SpringMVC框架在处理Web请求过程中所涉及的各个组件,包括HandlerMapping定义、Controller定义、ViewResolver定义等
。
该外部化的配置文件存在的默认路径也是/WEB- INF/
,名称需要参照web.xml
中定义的DispatcherServlet
的<servlet- name>
来决定。比如,我们当前定义的DispatcherServlet
对应的<servlet- name>
为controller,那么,默认的配置文件即对应/WEB-INF/controller- servlet.xml
,也就是说,DispatcherServlet
对应的默认配置文件名称,将在<servlet- name>
的值的基础上后缀-servlet.xml
。
如果我们定义DispatcherServlet
的<servlet-name>
为simplefx
,那么它的配置文件名称就应该为simplefx- servlet.xml
,以此类推。<servlet-name>-servlet.xml
与/WEB- INF/applicationContext.xml
相同,也符合Spring IoC容器配置文件格式。
只不过,相对于/WEB-INF/applicationContext.xml
来说,<servlet- name>servlet.xml
有自己的职责。它主要负责配置基于 SpringMVC框架的Web应用程序所使用的各种Web层组件
,具体内容看起来如下方代码所示。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd">
...
<bean id="handlerMapping" class="org.springframework.Web.servlet.handler.BeanNameUrlHanđlerMapping">
</bean>
<bean id="viewResolver" c1ass="org.springframework.Web.servlet.view.InternalResourceViewResolver">
<propertyname="prefix"value="/WEB-INF/jsp/"/>
<propertyname="suffix"value=".jsp"/>
</bean>
<bean name="/infoList.do"class="..InfoListController">
</bean>
...
</beans>
DispatcherServlet
启动之后将加载<servlet- name>-servlet.xml
配置文件,并构建相应的WebApplicationContext
。该WebApplicationContext
将之前通过ContextLoaderListener
加载的顶层WebApplicationContext
(ROOT
WebApplicationContext)作为父容器(Parent ApplicationContext)。
这样,如果需要,<servlet- name>-servlet.xml
中注册的各种面向Web层的组件,也可以注入来自顶层webApplicationContext
的依赖了。所以,我们也就不难想象出<servlet- name>-servlet.xml
对应的WebApplicationContext
和默认的/WEB- INF/applicationContext.xml
对应的顶层WebApplicationContext
之间的逻辑依赖关系了(见图23-7)。
即使Web层通常很薄,但随着Web应用程序的开发进程,单一的<servlet- name>-servlet.xml
文件有可能成为团队并行开发的瓶颈,或者其身材也可能会越加臃肿。出于这些因素考虑,我们需要寻求分割单一的<servlet- name>-servlet.xml
文件的解决方法,而DispatcherServlet
的contextConfigLocation
初始化参数就是为此而生的。下方代码给出了该初始化参数的使用配置代码示例。
<servlet>
<servlet-name>controller</servlet-name>
<servlet-class>org.springframework.Web.servlet.DispatcherServ1et</servlet-class>
<!--关键是下面4行-->
<init-param>
<param-name>contextConfigLocatlon</param-name>
<param-value>/WEB-INF/controller-servlet.xml,/WEB-INF/module1-servlet.xml,..</param-value>
</init-param>
<load-on-startup>2</load-on-startup>
</servlet>
<servlet-mapping>
<serv1et-name>controller</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
我们可以指定多个配置文件用逗号或空格来分隔。更多定制DispatcherServlet
的信息,可以参照Spring参考文档或者相应类的Javadoc。
现在,你已经对基于SpringMVC的Web应用程序都由哪些物理实体组成,以及如何定制它们有了一定的认识,那么,热身活动结束!
按部就班地开始工作
我们要实现的当日评价汇率显示流程可以简单描述,如图23-8所示。
从浏览器中点击后台管理画面的相应链接之后,Web请求将被发送到DispatcherServlet
进行处理。DispatcherServlet
将寻求相应的HandlerMapping
对Web请求进行分析,然后调用匹配结果对应的Controller
实现,具体到当前场景就是我们要实现的TTMRateListController。
TTMRateListController处理完毕将视图名称ttmRateList和模型数据一同返回,然后DispatcherServlet
则借助于相应的ViewResolver
,根据返回的视图名选择相应的视图(tmRateList.jsp)并显示,这就是整个流程。
下面则是实现这个Web处理流程的详细步骤,各位上眼瞧啦!
当日评价汇率Web处理流程实现步骤概况如下。
(1)配置基础设置
每个符合JavaEE规范的Web应用程序都需要符合相应的目录结构,如图23-6所示。工作之初,我们需要构建Web应用的基础结构。不过,现在的IDE通常都有良好的Web开发支持,使用EclipseIDE的话,添加WTP(Web Tools Platform)或者MyEclipse插件的支持即可。剩下的只是按照Wizard的说明构建一个Web应用的工程就行了,我们这里要建立的是simplefx工程。simplefx工程建立之后,我们还有如下两件事情要做。
a)配置web.xml
我们需要将org.springframework.Web.servlet.DispatcherServlet
和org.springframework.Web.context.ContextLoaderListener
通过<serv1et>和<listener>
元素添加到web.xml
部署描述符文件中,原因我们之前已经领教了。
另外,出于其他目的考虑,我们还可以添加相应的Filter
以及ServletContextListener
以处理字符编码和Log4j初始化等配置内容,这些完全根据当前应用程序的需求情况来决定。现在,我们的web.xml
看起来如下方代码所示。
<Web-app...>
<display-name>simplefx</display-name>
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>org.springframework.Web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-namne>
<param-value>UTF-8</param-value>
</inít-param>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<servlet-name>controller</servlet-name>
</filter-mapping>
<listener>
<listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
</listener>
<listener>
<listener-class>org.springframework.Web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>controller</servlet-name>
<servlet-class>org.springframework.Web,servlet.DispatcherServlet</servlet-class>
<load-on-startup>2</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>controller</servlet-name>
<ur1-pattern>*.do</ur1-pattern>
</servlet-mapping>
<welcome-file-list>
<we1come-file>index.jsp</welcome-file>
</welcome-file-list>
</Web-app>
其他配置元素可能随着内容的深入继续添加。
b)WebApplicationContext文件的添加
我们需要在/WEB-INF/
目录下添加org.springframe- work.Web.servlet.DispatcherServlet
和org.springframework.Web.context.ContextLoaderListener
对应的WebApplicationContext
配置文件(/WEB- INF/applicationContext.xml
和/WEB-INF/[servletname]-servlet.xml
)。
这完全可以从Spring下载文件包中的sample目录下任何一个Web应用程序中复制过来。只不过,需要根据命名规则修改相应的文件名称。当然,复制过来的文件内容最好是清空,只保留可以在当前应用中能够通用的配置内容。对于我们的simplefx来说,我们暂且清空对应的两个WebApplicationContext
配置文件内容。以上基础设施构建完成之后,我们开始“盖楼”。
(2)开发独立的业务逻辑
对于一个设计良好的Web应用程序来说,虽然Web层依赖于业务层对象,但业务层却不应该对Web层有任何的依赖。Web层只应该看作是公开业务逻辑的一种视角或者交互方式。这样做或者说这样看待系统的好处在于,业务层可以独立设计并实现,而不需要关心最终通过什么手段将服务公开给用户。
鉴于这样的理念,我们完全可以从业务层开始着手设计和实现我们的当日评价汇率显示逻辑,而且在稍后介绍Spring Remoting支持的时候,你将会更深地感受到这种看待系统的视角所带给我们的系统设计与实现的福利。
既然涉及评价汇率,那么首先需要设计并实现的对象即对应评价汇率的域对象(Domain Object),其简单定义如下方代码所示。
public class TTMRate implements Serializable {
private static final long serialVersionUID = 2641189625594925925L;
private TradeDate frontDate;
private String currencyPair;
private BigDecimal value;
public TTMRate(TradeDate frontDate, String currencyPair, BigDecimal value) {
this.frontDate = frontDate;
this.currencyPair = currencyPair;
this.value = value;
}
public TradeDate getFrontDate() {
return frontDate;
}
public void setFrontDate(TradeDate frontDate) {
this.frontDate = frontDate;
}
public String getCurrencyPair() {
return currencyPair;
}
public void setCurrencyPair(String currencyPair) {
this.currencyPair = currencyPair;
}
public BigDecimal getValue() {
return value;
}
public void setValue(BigDecimal value) {
this.value = value;
}
//其他方法定义,比如toString{)等
}
每个营业日内对应系统中可交易的货币对,都存在一个评价汇率,所以,TTMRate中定义了必需的三个字段分别对应 营业日、货币对和对应的评价汇率值 。
为了能够使各种客户端(当然包括Web客户端)能够获取当日的评价汇率,我们需要规定相应的接口并公开给相应的客户端使用。现在这个接口定义很简单,定义如下:
public interface ITTMRateService {
List<TTMRate>getTTMRatesToday();
}
只要客户端调用getTTMRatesToday()
方法,就将返回当前营业日内,系统所支持的所有货币对对应的评价汇率。单有接口并不能工作,我们还需要给出相应的实现。
正常情况下,我们需要结合相应的数据访问对象构建一个ITTMRateService
的实现,以便能够从系统数据库中获取真正生产环境下的数据。不过,我不打算这么做。出于简化实例考虑,我们现在只实现一个ITTMRateService
的Mock对象。定义如下:
public class MockTTMRateService implements ITTMRateService {
public List<TTMRate> getTTMRatesToday() {
TradeDate tradeDate20080302 = TradeDate.valueOf("20080302");
TTMRate USD_JPY = new TTMRate(tradeDate20080302, "USD/JPY",
new BigDecimal("121.53"));
TTMRate EUR_USD = new TTMRate(tradeDate20080302, " EUR/USD",
new BigDecimal("1.8950"));
List<TTMRate> rateList = new ArrayList<TTMRate>();
ratelist.add(USD_JPY);
ratelist.add(EUR_USD);
return ratelist;
}
}
我们假设当天营业日对应20080302,并将对应该日期的USD/JPY和EUR/USD两货币对的评价汇率信息添加到返回列表中。
整个Web应用的中间层服务支持,默认都是通过/WEB-INF/applicationContext.xml
注册(即ROOT
WebApplicationContext)。所以,现在我们要将ITTMRateService
的实现增加到Web应用程序项层容器的配置文件中,如下方代码所示。
<bean id="ttmRateService" class="cn.spring21.simplefx.service.MockTTMRateService">
</bean>
真实环境下,我们可能还需要向该容器配置文件中添加数据源(DataSource)等一系列依赖对象。
在此之后,我们才真正开始Web层的开发。
(3)添加Web请求入口
要让用户能够通过Web方式访问我们的评价汇率服务,我们得为用户提供一个入口点。首先在后台管理的主画面左侧的FunctionSet中添加超链接,如下所示:
<a href="<c:ur1 value='ttmRateList.do'/>">当日评价汇率</a>
在用户点击该链接之后,浏览器将以http://host:port/simplefx/ttmRateList.do
的形式向服务器发起Web请求。我们在web.xml
中将所有以.do
结尾的请求模式都映射给了DispatcherServlet
来处理。接下来的事情就由DispatcherServlet
接手了。
(4)添加HandlerMapping
DispatcherServlet
在接收到Web请求之后,将寻求相应的HandlerMapping
进行Web请求到具体的Controller
实现的匹配。所以,我们需要为DispatcherServlet
提供一个HandlerMapping
的实现。
SpringMVC框架默认提供了多个HandlerMapping
的实现。我们将在稍后详细介绍。不过,当前我们暂且使用org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping
,它将根据URL与Controller的bean定义的名称进行匹配。
现在我们将它添加到DispatcherServlet
特定的WebApplicationContext
中,即/WEB-INF/controller- servlet.xml
,如下所示:
<bean id="handlerMapping" class="org.springframework.Web.servlet.handler.BeanNameUrlHandlerMapping">
</bean>
实际上,如果没有配置任何HandlerMapping
的话,SpringMVC也会默认使用BeanNameUrlHandlerMapping
进行URL到具体Controller的匹配。所以,以上的bean定义并非必须的。
但是,这样做的目的是要明确告知你,Dispatcherservlet
所使用的HandlerMapping
要在哪里进行配置。稍后你还将看到,如果默认的HandlerMapping
的匹配模式并不讨人喜欢,我们还可以替换掉它,转而使用自己喜欢的HandlerMapping
实现。
总之,我们使用BeanNameUrlHandlerMapping
进行URL到具体Controller的匹配之后,BeanNameUrlHandlerMapping
将根据http://host:port/simplef/ttmRateList.do
这一URL信息,在当前容器内寻找名称为/ttmRateList.do
的Controller定义。这将是我们下一步要实现的东西。
(5)实现对应的Controller并添加到配置文件
针对当前评价汇率的Web请求,我们需要实现一个Controller来处理它。通常情况下,我们会扩展SpringMVC的org.springframework.Web.servlet.mvc.AbstractController
来实现具体的Controller。
正如我们的处理流程图(图23-8)所演示的那样,我们的Controller名称为TTMRateListController
,具体定义如下方代码所示。
public class TIMRateListController extends AbstractController {
private ITTMRateService ttmRateService;
private String viewName;
@Override
protected ModelAndView handleRequestInternal(HttpServletRequest request,
HttpServletResponse response) throws Exception {
List<TTMRate> ttmRateList = getTtmRateService().getTTMRatesToday()
ModelAndView mav = new ModelAndView(getVi ewName());
mav.addobject(" ttmRates", ttmRatelist);
return mav;
}
public ITTMRateService getTtmRateService() {
return ttmRateService;
}
public void setTtmRateService(ITTMRateService t tmRateService) {
this.ttmRateService = ttmRateService;
}
public String getViewName() {
return viewName;
}
public void setViewName(String viewName) {
this.viewName = viewName;
}
}
在TTMRateListController
中,我们引用了ITTMRateService
并借助它获取了当日的评价汇率列表。
viewName
是将要转向的逻辑视图名称,可以将它写死到代码。不过,通过WebApplicationContext
的依赖注入,我们可以获取更多的灵活度。在视图名称和要显示的模型数据全都获取之后,我们构造了一个ModelAndView
来封装这两部分信息,并返回。
只是实现了TTMRateListController
,DispatcherServlet
并不会知道它的存在,也不会知道如何引用到它,所以,我们还得将TTMRateListController
添加到DispatcherServlet
特定的WebApplicationContext
中。现在,/WEB- INF/controller-servlet.xml
看起来更加充实了,如下所示。
<bean id="handlerMapping" class="org.springframework.Web.servlet.handler.BeanNameUrlHandlerMapping">
</bean>
<bean name="/ttmRatelist.do" class="..TTMRateLístController">
<property name="ttmRateService" ref="ttmRateService"/>
<property name="viewName" value="ttmRateList"/>
</bean>
这里需要注意的是,TTMRateListController
的ttmRateService
注入的依赖来自org.springframework.Web.context.ContextLoaderListener
加载的顶层WebApplicationContext
。
我们说过,DispatcherServlet
特定的WebApplicationContext
以顶层WebApplicationContext
作为父容器。另外,BeanNameUrlHandlerMapping
要求相应的Controller的bean定义名称以/
开头,所以,我们得使用<bean>
的name
属性来标志Controller对应的bean定义。
(6)添加ViewResover
TTMRateListController
通过ModelAndView
返回了一个逻辑视图名ttmRateList
,而DispatcherServlet
要根据这个逻辑视图名查找相应的视图实现,就需要一个ViewResolver
来帮它。
目前我们还不能在DispatcherServlet
的特定WebApplicationContext
中找到一个ViewResolver
的实现,所以至少得添加一个,否则也太令DispatcherServlet
失望了。
SpringMVC也为ViewResolver提供了多种实现,我们当前决定使用比较普及的JST/JSTL作为视图技术,所以,将org.springframework.Web.servlet.view.InternalResourceViewResolver
添加到了DispatcherServlet
特定的WebApplicationContext
中,如下所示:
<bean id="handlerMapping" class="org.springframework.Web.servlet.handler.BeanNameUrlHandlerMapping">
</bean>
<bean name="/ttmRatelist.do" class="..TTMRateLístController">
<property name="ttmRateService" ref="ttmRateService"/>
<property name="viewName" value="ttmRateList"/>
</bean>
<!--重点是下面-->
<bean id="viewResolver" class="org.springframework.Web.servlet.view.InternalResourceViewResolver">
<property name="prefix" va1ue="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
我们为InternalResourceViewResolver
指定了prefix和suffix两个属性的自定义值。这样,当我们的逻辑视图名(viewName)为ttmRateList
的时候,InternalResourceViewResolver
将去寻找[prefix]+viewName+[suffix]
名称的视图模板文件,也就是/WEB- INF/jp/tmRateList.jsp
。不过看来该JSP文件还没有创建。
(7)实现相应视图
我们得提供InternalResourceViewResolver
根据逻辑视图名所查找的视图模板文件/WEB- INF/jsp/ttmRateList.jsp
。我们通过相应的IDE在/WEB- INF/jsp/
目录下创建一个ttmRateList.jsp文件,代码就不贴过来了。
本章小结
在深入SpringMVC腹地之前,为了帮助你尽快融入到SpringMVC的世界中,我们首先从总体上对SpringMVC的设计和实现流程进行了简单的介绍。
实例通常是快速了解一件事物的最好方式,所以,我们在简单分析了基于SpringMVC的Web应用程序物理结构之后,一起开发了我们的实例应用simplefx。接下来,我们将在simplefx的基础上,进一步扩展视野,引入SpringMVC中的概念。