天天看点

《Spring MVC学习指南(第2版)》——2.3 模型2之Servlet控制器

本节书摘来自异步社区《spring mvc学习指南(第2版)》一书中的第2章,第2.3节,作者:【美】paul deck著,更多章节内容可以访问云栖社区“异步社区”公众号查看

为了便于对模型2有一个直观的了解,本节将展示一个简单模型2应用。实践中,模型2的应用非常复杂。

示例应用名为appdesign1,其功能设定为输入一个产品信息。具体为:用户填写产品表单(图2.2)并提交;示例应用保存产品并展示一个完成页面,显示已保存的产品信息(见图2.3)。

《Spring MVC学习指南(第2版)》——2.3 模型2之Servlet控制器

图2.2 产品表单

《Spring MVC学习指南(第2版)》——2.3 模型2之Servlet控制器

图2.3 产品详细页

示例应用支持如下两个action。

(1)展示“添加产品”表单。该action将图2.2中的输入表单发送到浏览器上,其对应的uri应包含字符串input-product。

(2)保存产品并返回如图2.3所示的完成页面,对应的uri必须包含字符串save-product。

示例应用由如下组件构成:

(1)一个product类,作为product的领域对象。

(2)一个productform类,封装了html表单的输入项。

(3)一个controllerservlet类,本示例应用的控制器。

(4)一个saveproductaction类。

(5)两个jsp页面(productform.jsp和product detail.jsp)作为视图。

(6)一个css文件,定义了两个jsp页面的显示风格。

示例应用目录结构如图2.4所示。

《Spring MVC学习指南(第2版)》——2.3 模型2之Servlet控制器

图2.4 app02a目录结构

下面详细介绍示例应用的每个组件。

2.3.1 product类

product实例是一个封装了产品信息的javabean。product类(见清单2.1)包含3个属性:productname、description和price。

清单2.1 product类

product类实现了java.io.serializable接口,其实例可以安全地将数据保存到httpsession中。根据serializable的要求,product实现了一个serialversionuid属性。

2.3.2 productform类

表单类与html表单相映射,是后者在服务端的代表。productform类(见清单 2.2)包含了一个产品的字符串值。productform类看上去同product类相似,这就引出一个问题:productform类是否有存在的必要。

实际上,表单对象会传递servletrequest给其他组件,类似validator(本章后面会介绍)。而servletrequest是一个servlet层的对象,不应当暴露给应用的其他层。

另一个原因是,当数据校验失败时,表单对象将用于保存和展示用户在原始表单上的输入。2.5节将会详细介绍应如何处理。

注意:

 

大部分情况下,一个表单类不需要实现serializable接口,因为表单对象很少保存在httpsession中。

清单2.2 productform类

2.3.3 controllerservlet类

controllerservlet类(见清单2.3)继承自javax.servlet.http.httpservlet类。其doget和dopost方法最终调用process方法,该方法是整个servlet控制器的核心。

可能有人好奇,为何这个servlet控制器命名为controllerservlet,实际上,这里遵从了一个约定:所有servlet的类名称都带有servlet后缀。

清单2.3 controllerservlet类

ontrollerservlet的process方法处理所有输入请求。首先是获取请求uri和action名称。

在本示例应用中,action值只会是input-product或save-product。

接着,process方法执行如下步骤。

(1)创建并根据请求参数构建一个表单对象。save-product操作涉及3个属性:name、description和price。然后创建一个领域对象,并通过表单对象设置相应属性。

(2)执行针对领域对象的业务逻辑。

(3)转发请求到视图(jsp页面)。

process方法中判断action的if代码块如下:

对于input-product,无需任何操作,而针对save-product,则创建一个productform对象和product对象,并将前者的属性值复制到后者。这个步骤中,针对空字符串的复制处理将留到稍后的2.5节处理。

再次,process方法实例化saveproductaction类,并调用其save方法。

然后,将product对象放入httpservletrequest对象中,以便对应的视图能访问到。

最后,process方法转到视图,如果action是product_input,则转到productform.jsp页面,否则转到productdetails.jsp页面。

2.3.4 action类

这个应用中只有一个action类,负责将一个product持久化,例如数据库。这个action类名为saveproductaction(见清单2.4)。

清单2.4 saveproductaction类

在这个示例中,saveproductaction类的save方法是一个空实现。我们会在本章后续章节中实现它。

2.3.5 视图

示例应用包含两个jsp页面。第一个页面productform.jsp对应于input-product操作,第二个页面productdetails.jsp对应于save-product操作。productform.jsp以及productdetails.jsp页面代码分别见清单2.5和清单2.6。

清单2.5 productform.jsp

注意

不要用html tabel来布局表单,用css。

价格输入域的step属性要求浏览器允许输入小数数字。

清单2.6 productdetails.jsp

productform.jsp页面包含了一个html表单。productdetails.jsp页面通过表达式语言(el)访问httpservletrequest所包含的product对象。

作为模型2的一个应用,本示例应用可以通过如下几种方式避免用户通过浏览器直接访问jsp页面。

将jsp页面都放到web-inf目录下。web-inf目录下的任何文件或子目录都受保护,无法通过浏览器直接访问,但控制器依然可以转发请求到这些页面。

利用一个servlet filter过滤jsp页面。

在部署描述符中为jsp页面增加安全限制。这种方式相对容易些,无需编写filter代码。

2.3.6 测试应用

假定示例应用运行在本机的8080端口上,则可以通过如下url访问应用:

<code>http://localhost:8080/appdesign1/input-product</code>

浏览器将显示图2.2的内容。

完成输入后,表单提交到如下服务端url上:

<code>http://localhost:8080/appdesign1/save-product</code>