17.4 第一个Spring MVC应用

17.4 第一个Spring MVC应用

本章的示例应用程序app17a展示了基本的Spring MVC应用。该应用程序同第16章学习的app16b应用非常相似,以便展示Spring MVC是如何工作的。app17a应用也有两个控制器是类似于app17b的控制器类。

17.4.1 目录结构

以下为app17a的目录结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
E:\workspace_web\app17a
├─pom.xml
└─src
├─main
│ ├─java
│ │ └─app17a
│ │ ├─controller
│ │ │ ├─InputProductController.java
│ │ │ └─SaveProductController.java
│ │ ├─domain
│ │ │ └─Product.java
│ │ └─form
│ │ └─ProductForm.java
│ ├─resources
│ └─webapp
│ ├─css
│ │ └─main.css
│ ├─index.jsp
│ ├─META-INF
│ │ └─MANIFEST.MF
│ └─WEB-INF
│ ├─jsp
│ │ ├─ProductDetails.jsp
│ │ └─ProductForm.jsp
│ ├─lib
│ ├─springmvc-servlet.xml
│ └─web.xml
└─test
└─java

这是一个基于Maven的Java Web项目,依赖如下所示,注意,。特别需要注意的是spring-webmvc-x.y.z.jar文件,其中包含了DispatcherServlet的类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<!-- Spring MVC依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>4.3.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>4.3.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>4.3.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>4.3.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.3.9.RELEASE</version>
</dependency>

还要注意**Spring MVC依赖于Apache Commons Logging组件,没有它,SpringMVC应用程序将无法正常工作**。可以从以下网址下载这个组件:
http://commons.apache.org/proper/commons-loggins/download_logging.cgi
或者使用Maven引入,如下所示:

1
2
3
4
5
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.1.3</version>
</dependency>

本示例应用的所有JSP页面都存放在/WEB-INF/jsp目录下,确保无法被客户端直接访问。

17.4.2 部署描述符文件和Spring MVC配置文件

部署描述符(web.xml)文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" 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_3_0.xsd">
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>*.action</url-pattern>
</servlet-mapping>
</web-app>

这里告诉了Servlet/JSP容器,我们将使用Spring MVCDispatcherServlet,并通过配置url-pattern元素值为“/”,将所有的URL映射到该servlet。由于servlet元素下没有init-param元素,所以Spring MVC的配置文件在/WEB-INF文件夹下,并按照通常的命名约定。
下面,我们来看一下清单17.2所示的Spring MVC配置文件(springmvc-servlet.xml)。

Spring MVC配置文件springmvc-servlet.xml

1
2
3
4
5
6
7
8
9
10
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean name="/product_input.action"
class="app17a.controller.InputProductController" />
<bean name="/product_save.action"
class="app17a.controller.SaveProductController" />
</beans>

这里声明了InputProductControllerSaveProductController两个控制器类,并分别映射到/product_input.action/product_save.action。两个控制器是将在下一节讨论。

17.4.3 Controller

app17a应用程序有InputProductControllerSaveProductController两个“传统”风格的控制器,分别实现了Controller接口。代码如下.

InputProductController类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package app17a.controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;
public class InputProductController
implements
Controller
{
private static final Log logger = LogFactory
.getLog(InputProductController.class);
@Override
public ModelAndView handleRequest(HttpServletRequest request,
HttpServletResponse response) throws Exception
{
logger.info("InputProductController called");
//返回一个视图
return new ModelAndView("/WEB-INF/jsp/ProductForm.jsp");
}
}

InputProductController类的handleRequest方法只是返回一个ModelAndView,包含一个视图,且没有模型。因此,该请求将被转发到/WEB-INF/jsp/ProductForm.jsp页面。

SaveProductController类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
package app17a.controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;
import app17a.domain.Product;
import app17a.form.ProductForm;
public class SaveProductController
implements
Controller
{
private static final Log logger = LogFactory
.getLog(SaveProductController.class);
@Override
public ModelAndView handleRequest(HttpServletRequest request,
HttpServletResponse response) throws Exception
{
logger.info("SaveProductController called");
ProductForm productForm = new ProductForm();
// populate action properties
productForm.setName(request.getParameter("name"));
productForm
.setDescription(request.getParameter("description"));
productForm.setPrice(request.getParameter("price"));
// create model
Product product = new Product();
product.setName(productForm.getName());
product.setDescription(productForm.getDescription());
try
{
product
.setPrice(Float.parseFloat(productForm.getPrice()));
} catch (NumberFormatException e)
{}
// insert code to save Product
//返回视图(jsp页面)的路径,模型的名称,模型(product对象)
return new ModelAndView("/WEB-INF/jsp/ProductDetails.jsp",
"product",
product);
}
}

SaveProductController类的handleRequest方法中,首先用请求参数创建一个ProductForm对象;然后,它根据ProductForm对象创建Product对象。由于ProductFormprice属性是一个字符串,而其在Product类对应的是一个float,此处类型转换是必要的。第18章,我们将学习在Spring MVC中如何省去ProductForm对象,使编程更简单。
SaveProductControllerhandleRequest方法最后返回的ModelAndView模型包含了视图的路径模型名称以及模型product对象)。该模型将提供给目标视图,用于界面显示。

17.4.4 View

app17a应用程序中包含两个JSP页面:ProductForm.jsp页面和ProductDetails.jsp页面

ProductForm.jsp页面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<!DOCTYPE HTML>
<html>
<head>
<title>Add Product Form</title>
<style type="text/css">
@import url(css/main.css);
</style>
</head>
<body>
<div id="global">
<form action="product_save.action" method="post">
<fieldset>
<legend>Add a product</legend>
<p>
<label for="name">Product Name: </label> <input
type="text" id="name" name="name" tabindex="1">
</p>
<p>
<label for="description">Description: </label> <input
type="text" id="description" name="description"
tabindex="2">
</p>
<p>
<label for="price">Price: </label> <input
type="text" id="price" name="price" tabindex="3">
</p>
<p id="buttons">
<input id="reset" type="reset" tabindex="4">
<input id="submit" type="submit" tabindex="5"
value="Add Product">
</p>
</fieldset>
</form>
</div>
</body>
</html>

此处不适合讨论HTMLCSS,但需要强调的是项目中的HTML是经过适当设计的,并且没有使用<table>来布局输入字段。

ProductDetails.jsp页面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<!DOCTYPE HTML>
<html>
<head>
<title>Save Product</title>
<style type="text/css">
@import url(css/main.css);
</style>
</head>
<body>
<div id="global">
<h4>The product has been saved.</h4>
<p>
<h5>Details:</h5>
Product Name: ${product.name}<br /> Description:
${product.description}<br /> Price: $${product.price}
</p>
</div>
</body>
</html>

ProductDetails.jsp页面通过模型属性名“product”来访问由SaveProductController传入的Product对象。这里用JSP表达式语言来显示Product对象的各种属性。

17.4.5 测试应用

现在,在浏览器中输入如下URL来测试应用:
http://localhost:8080/app17a/product_input.action
会看到类似于下图所示的产品表单页面,在空字段中输入相应的值后单击Add Product(添加产品)按钮,
这里有一张图片
会在下一页中看到产品属性,如下图所示:
这里有一张图片