1. 简介
本文档的PDF版本可在此处获取 |HERE|。
本文档中的示例可在此处查看 |HERE|。
本文旨在通过实例介绍 Spring MVC 的核心概念。Spring MVC 是一个 Java Web 框架,为基于 MVC(模型-视图-控制器)模式开发 Web 应用程序提供了框架。Spring MVC 是 Spring 生态系统 [http://projects.spring.io/spring-framework/] 的一部分。 我们还将介绍 Thymeleaf 视图引擎 [http://www.thymeleaf.org/]。
本课程面向已扎实掌握 Java 语言的读者。无需具备 Web 编程的先备知识。
尽管本文内容详尽,但可能仍不完整。Spring 是一个庞大的框架,包含众多分支。若要进一步了解 Spring MVC,可参考以下资源:
- Spring 框架参考文档 [http://docs.spring.io/spring/docs/current/spring-framework-reference/pdf/spring-framework-reference.pdf];
- 大量 Spring 教程可参见 [http://spring.io/guides]
- 专门介绍 Spring 的 [developpez.com] 网站 [http://spring.developpez.com/]。
本文档的编写旨在方便读者在没有电脑的情况下阅读,因此包含大量屏幕截图。
1.1. 来源
本文主要参考了以下两个来源:
- [《ASP.NET MVC 实例入门》]。Spring MVC 和 ASP.NET MVC 是两个相似的框架,后者是在前者问世很久之后才开发的。为了对比这两个框架,我遵循了 ASP.NET MVC 文档中的相同步骤;
- 目前(2014年12月),ASP.NET MVC文档中尚未包含带解决方案的案例研究。本文采用了[AngularJS / Spring 4 教程]中的案例,并进行了如下修改:
- [AngularJS / Spring 4 教程]中的案例是一个客户端/服务器应用程序,其中服务器是使用Spring MVC构建的Web服务/JSON,客户端则是AngularJS客户端;
- 在本文档中,我们使用相同的 Web 服务/JSON,但客户端采用两层 Web 应用程序架构 [jQuery 客户端] / [Web 服务/JSON];
除了这些资料外,我还通过互联网搜索了相关问题的答案。其中,网站 [http://stackoverflow.com/] 对我的帮助尤为大。
1.2. 使用的工具
以下示例已在以下环境中进行测试:
- Windows 8.1 Pro 64 位系统;
- JDK 1.8;
- Spring Tool Suite 3.6.3 集成开发环境(参见第 9.3 节);
- Chrome 浏览器(未使用其他浏览器);
- Chrome 扩展程序 [Advanced Rest Client](参见第 9.6 节);
关于 JDK 1.8 的说明:案例研究中的一个方法使用了 Java 8 中 [java.lang] 包中的方法。
所有示例均为 Maven 项目,可在 Eclipse、IntelliJ IDEA 或 NetBeans 中打开。下文中的屏幕截图均来自 Spring Tool Suite IDE(Eclipse 的一个变体)。
1.3. 示例
示例已作为可下载的 ZIP 文件发布于 |此处|。
![]() |
要将所有项目加载到 STS 中,请按以下步骤操作:
![]() |
![]() |
- 在 [1-3] 中,导入 Maven 项目;
![]() |
- 在 [4] 中,指定 examples 文件夹;
- 在 [5] 中,选中该文件夹中的所有项目;
- 在 [6] 中,确认;
- 在 [7] 中,导入的项目;
1.4. Spring MVC 在 Web 应用程序中的作用
让我们将 Spring MVC 置于 Web 应用程序的开发背景中。通常,它将基于如下所示的多层架构构建:
![]() |
- [Web] 层是与 Web 应用程序用户直接交互的层。用户通过浏览器中显示的网页与 Web 应用程序进行交互。Spring MVC 位于该层,且仅位于该层;
- [Web] 层实现应用程序的业务逻辑,例如计算工资或生成发票。该层通过 [Web] 层获取用户数据,并通过 [DAO] 层从 DBMS 获取数据;
- [DAO](数据访问对象)层、[ORM](对象关系映射器)层以及 JDBC 驱动程序负责管理对 DBMS 中数据的访问。[ORM] 层充当 [DAO] 层处理的对象与关系型数据库中表的行和列之间的桥梁。在此我们将使用 Hibernate ORM。 一种名为 JPA(Java 持久化 API)的规范允许我们抽象掉所使用的具体 ORM,前提是该 ORM 实现了这些规范。Hibernate 及其他 Java ORM 均符合这一条件。因此,此后我们将把 ORM 层称为 JPA 层;
- 各层的集成由 Spring 框架负责;
下文提供的示例大多仅使用单一层,即 [Web] 层:
![]() |
不过,本文最后将介绍如何构建一个多层Web应用程序:
![]() |
浏览器将连接到一个使用 Spring MVC / Thymeleaf 实现的 [Web1] 应用程序,该应用程序将从同样使用 Spring MVC 实现的 [Web2] Web 服务中获取数据。第二个 Web 应用程序将访问数据库。
1.5. Spring MVC 开发模型
Spring MVC 通过以下方式实现 MVC(模型–视图–控制器)架构模式:
![]() |
客户端请求的处理流程如下:
- 请求 - 请求的 URL 通常采用 http://machine:port/contexte/Action/param1/param2/....?p1=v1&p2=v2&... 的形式。[前端控制器] 会通过配置文件或 Java 注解,根据 URL 中的 [Action] 字段,将请求“路由”到正确的控制器及其内部的相应操作。 URL 的其余部分 [/param1/param2/...] 由可选参数组成,这些参数将传递给 Action。此处的 MVC 中的 C 指的是 [前端控制器、控制器、Action] 这一链条。如果没有控制器能处理所请求的 Action,Web 服务器将返回“未找到所请求的 URL”的响应。
- 处理
- 所选操作可以使用 [前端控制器] 传递给它的参数。这些参数可能来自以下几个来源:
- URL 的路径 [/param1/param2/...],
- URL 中的 [p1=v1&p2=v2] 参数,
- 浏览器随请求提交的参数;
- 在处理用户请求时,操作可能需要调用 [业务] 层 [2b]。一旦客户端的请求被处理完毕,可能会触发各种响应。一个典型的例子是:
- 若请求无法正确处理,则返回错误页面
- 否则则返回确认页面
- 操作会指示特定的视图进行渲染 [3]。该视图将显示称为视图模型的数据。这就是 MVC 中的“M”。操作将创建该视图模型 [2c] 并指示视图进行渲染 [3];
- 响应——选定的视图 V 使用操作生成的模型 M 来初始化其必须发送给客户端的 HTML 响应中的动态部分,然后发送该响应。
现在,让我们澄清 MVC Web 架构与分层架构之间的关系。根据我们对模型的定义不同,这两个概念可能相关,也可能无关。让我们考虑一个单层的 Spring MVC Web 应用程序:
![]() |
如果我们使用 Spring MVC 来实现 [Web] 层,那么我们确实拥有了 MVC Web 架构,但并非多层架构。在此情况下,[Web] 层将处理所有事务:呈现、业务逻辑和数据访问。这些工作将由 Action 类来完成。
现在,让我们考虑一种多层Web架构:
![]() |
Web 层可以在不使用框架且不遵循 MVC 模型的情况下实现。这样我们就得到了一个多层架构,但 Web 层并未实现 MVC 模型。
例如,在 .NET 环境中,上述 [Web] 层可以使用 ASP.NET MVC 来实现,从而形成一个具有 MVC 风格 [Web] 层的分层架构。一旦完成,我们就可以将这个 ASP.NET MVC 层替换为经典的 ASP.NET 层(WebForms),同时保持其余部分(业务逻辑、DAO、ORM)不变。 此时,我们便获得了一种分层架构,其[Web]层不再基于MVC。
在 MVC 中,我们曾提到 M 模型即 V 视图所呈现的数据集。关于 MVC 中 M 模型的另一种定义如下:
![]() |
许多作者认为,位于 [Web] 层右侧的部分构成了 MVC 中的 M 模型。为避免歧义,我们可以将:
- 当提及[Web]层右侧的所有内容时,称之为领域模型
- 当指代视图 V 所显示的数据时,称之为视图模型
下文中,“M模型”一词将专指视图V的模型。
1.6. 首个 Spring MVC 项目
从现在起,我们将使用 Spring Tool Suite (STS) IDE,这是一个为 Spring 定制的 Eclipse 版本。网站 [http://spring.io/guides] 提供了入门教程,用于探索 Spring 生态系统。我们将遵循其中一个教程,学习 Spring MVC 项目所需的 Maven 配置。
注意:大多数初学者可能无法完全掌握项目的细节。这并不重要。这些细节将在本文档后文进行解释。我们只需按照步骤操作即可。
1.6.1. 演示项目
![]() |
- 在 [1] 中,我们导入了一个 Spring 指南;
![]() |
- 在 [2] 中,我们选择了 [Serving Web Content] 示例;
- 在 [3] 中,我们选择 Maven 项目;
- 在 [4] 中,我们选择指南的最终版本;
- 在 [5] 中,我们确认;
- 在 [6] 中,导入项目;
让我们来检查一下该项目,首先从其 Maven 配置开始。
1.6.2. Maven 配置
[pom.xml] 文件内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework</groupId>
<artifactId>gs-serving-web-content</artifactId>
<version>0.1.0</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.1.9.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
</dependencies>
<properties>
<start-class>hello.Application</start-class>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>spring-milestone</id>
<url>https://repo.spring.io/libs-release</url>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>spring-milestone</id>
<url>https://repo.spring.io/libs-release</url>
</pluginRepository>
</pluginRepositories>
</project>
- 第 6–8 行:Maven 项目的属性。缺少一个指定 Maven 构建生成的文件类型的 [<packaging>] 标签。在缺少该标签的情况下,将使用 [jar] 类型。因此,该应用程序是一个基于控制台的可执行应用程序,而不是 Web 应用程序(Web 应用程序的打包类型应为 [war]);
- 第 10–14 行:该 Maven 项目有一个父项目 [spring-boot-starter-parent]。它定义了该项目的大部分依赖项。这些依赖项可能已经足够,此时不会添加额外依赖;也可能不够,此时会添加缺失的依赖项;
- 第 17–20 行:[spring-boot-starter-thymeleaf] 构建产物包含 Spring MVC 项目所需的库,这些库用于配合名为 [Thymeleaf] 的视图引擎使用。该构建产物包含大量库,其中包括用于嵌入式 Tomcat 服务器的库。应用程序将在该服务器上运行;
此配置中包含的库数量众多:
![]() | ![]() |
上图所示为 Tomcat 服务器的归档文件。
Spring Boot 是 Spring 生态系统 [http://projects.spring.io/spring-boot/] 中的一个分支。该项目旨在最大限度地减少 Spring 项目所需的配置。为此,Spring Boot 会根据项目类路径中存在的依赖项进行自动配置。Spring Boot 提供了许多开箱即用的依赖项。 例如,前一个 Maven 项目中包含的 [spring-boot-starter-thymeleaf] 依赖项,就涵盖了使用 [Thymeleaf] 视图引擎的 Spring MVC 应用程序所需的所有依赖项。凭借以下两项特性:
- 即用型依赖项;
- 基于这些依赖项和“合理”默认值的自动配置,您可以非常快速地构建出一个可运行的 Spring MVC 应用程序。本文所研究的项目正是如此;
1.6.3. Spring MVC 应用程序的架构
Spring MVC 实现了 MVC(模型-视图-控制器)架构模式:
![]() |
客户端请求的处理流程如下:
- 请求 - 请求的 URL 格式为 http://machine:port/contexte/Action/param1/param2/....?p1=v1&p2=v2&... [Dispatcher Servlet] 是 Spring 框架中负责处理传入 URL 的类。它会将 URL “路由”到应处理该请求的操作。这些操作是称为 [控制器] 的特定类中的方法。 此处的 MVC 中的 C 代表 [Dispatcher Servlet、Controller、Action] 这一链条。如果未配置任何 Action 来处理传入的 URL,[Dispatcher Servlet] 将返回请求的 URL 未找到(404 NOT FOUND 错误);
- 处理
- 被选中的 Action 可以使用 [Dispatcher Servlet] 传递给它的参数。这些参数可能来自多个来源:
- URL 的路径 [/param1/param2/...],
- URL 参数 [p1=v1&p2=v2],
- 浏览器随请求提交的参数;
- 在处理用户请求时,操作可能需要调用 [业务] 层 [2b]。一旦客户端的请求被处理完毕,可能会触发各种响应。一个典型的例子是:
- 若请求无法正确处理,则返回错误页面
- 否则则显示确认页面
- 操作会指示显示特定的视图 [3]。该视图将显示称为视图模型的数据。这就是 MVC 中的 M。操作将创建此 M 模型 [2c] 并指示显示 V 视图 [3];
- 响应——选定的视图 V 使用操作生成的模型 M 来初始化其必须发送给客户端的 HTML 响应中的动态部分,然后发送该响应。
我们将在此研究项目中探讨这些不同元素。
1.6.4. 控制器 C
![]() |
导入的应用程序包含以下控制器:
package hello;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
@Controller
public class GreetingController {
@RequestMapping("/greeting")
public String greeting(@RequestParam(value="name", required=false, defaultValue="World") String name, Model model) {
model.addAttribute("name", name);
return "greeting";
}
}
- 第 8 行:[@Controller] 注解将 [GreetingController] 类定义为 Spring 控制器,这意味着其方法已被注册以处理 URL。Spring 控制器是单例模式,仅会创建一个实例;
- 第 11 行:[@RequestMapping] 注解指定了该方法处理的 URL,本例中为 [/greeting]。稍后我们将看到,该 URL 可以带参数,并且可以获取这些参数;
- 第 12 行:该方法接受两个参数:
- [String name]:该参数由处理请求中的 [name] 参数初始化,例如 [/greeting?name=alfonse]。该参数为可选 [required=false],若未提供,则 [name] 参数将取值 'World' [defaultValue="World"],
- [Model model] 是一个视图模型。它被空传入,而填充该模型是该操作(greeting 方法)的职责。该模型将被传递给该操作将渲染的视图。因此它是一个视图模型;
- 第 13 行:将 [name] 的值放入视图模型中。[Model] 类的工作方式类似于字典;
- 第 14 行:该方法返回应显示已构建模型的视图名称。视图的确切名称取决于 [Thymeleaf] 配置。若未进行此类配置,此处显示的视图将为 [/templates/greeting.html],其中 [templates] 文件夹必须位于项目类路径的根目录下;
让我们查看我们的 Eclipse 项目:
![]() |
[src/main/java] 和 [src/main/resources] 这两个文件夹的内容都会被添加到项目的类路径中。对于 [src/main/java],Java 源代码的编译版本将被放置在此处。而 [src/main/resources] 文件夹中的内容则会未经修改地直接添加到类路径中。 因此,我们可以看到 [templates] 文件夹将位于项目的类路径中 [1]。
您可以在 Eclipse 的 [导航器] 窗口 [窗口 / 显示视图 / 其他 / 常规 / 导航器] 中验证这一点 [2-3]。[target] 文件夹是在编译(或“构建”)项目时生成的。[classes] 文件夹代表类路径的根目录。您可以看到 [templates] 文件夹就在那里。
1.6.5. 视图 V
在 MVC 架构中,我们刚才已经了解了控制器 C 和模型 M。视图 V 在这里由以下 [greeting.html] 文件表示:
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Getting Started: Serving Web Content</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<p th:text="'Hello, ' + ${name} + '!'" />
</body>
</html>
- 第 2 行:Thymeleaf 标签命名空间;
- 第 8 行:一个带有 Thymeleaf 属性的 <p> 标签(段落)。[th:text] 属性用于设置段落的内容。在字符串内部,我们使用了表达式 [${name}]。这意味着我们希望获取视图模板中 [name] 属性的值。现在,请回想一下,该属性是由操作添加到模型中的:
model.addAttribute("name", name);
第一个参数设置属性的名称,第二个参数设置其值。
1.6.6. 执行
![]() |
[Application.java] 类是该项目的可执行类。其代码如下:
package hello;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.SpringApplication;
import org.springframework.context.annotation.ComponentScan;
@ComponentScan
@EnableAutoConfiguration
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
- 第 11 行:该类可通过专用于控制台应用程序的 [main] 方法执行。第 12 行的 [SpringApplication] 类将启动依赖项中存在的 Tomcat 服务器,并在其上部署 Web 服务;
- 第 4 行:我们可以看到 [SpringApplication] 类属于 [Spring Boot] 项目;
- 第 12 行:第一个参数是用于配置项目的类,第二个参数包含任何附加参数;
- 第 8 行:[@EnableAutoConfiguration] 注解指示 Spring Boot 配置该项目;
- 第 7 行:[@ComponentScan] 注解会扫描包含 [Application] 类的目录以查找 Spring 组件。将找到一个组件:[GreetingController] 类,该类带有 [@Controller] 注解,因此成为 Spring 组件;
现在运行该项目:
![]() |
我们得到以下控制台日志:
- 第 13 行:Tomcat 服务器在端口 8080 上启动(第 12 行);
- 第 17 行:存在 [DispatcherServlet] Servlet;
- 第 20 行:已发现方法 [hello.GreetingController.greeting] 及其处理的 URL [/greeting];
要测试该 Web 应用程序,请访问 URL [http://localhost:8080/greeting]:
![]() | ![]() |
查看服务器发送的 HTTP 头部信息可能会很有趣。为此,我们将使用名为 [Advanced Rest Client] 的 Chrome 插件(参见第 9.6 节):
![]() |
- 在 [1] 中,请求的 URL;
- 在 [2] 中,使用了 GET 方法;
- 在 [3] 中,服务器表明其发送的是 HTML 格式的响应;
- 在 [4] 中,是 HTML 响应;
- 在 [5] 中,我们请求相同的 URL,但这次使用 POST 请求;
- 在 [7] 中,信息以 [urlencoded] 格式发送至服务器;
- 在 [6] 中,name 参数及其值;
- 在 [8] 中,浏览器告知服务器其正在发送 [urlencoded] 格式的数据;
- 在 [9] 中,来自服务器的 HTML 响应;
![]() | ![]() | ![]() |
1.6.7. 创建可执行归档文件
可以在 Eclipse 外部创建可执行归档文件。所需的配置位于 [pom.xml] 文件中:
<properties>
<start-class>hello.Application</start-class>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
- 第 7–10 行定义了用于创建可执行归档文件的插件;
- 第2行 定义了项目的可执行类;
操作步骤如下:
![]() |
- 在 [1] 中:我们运行一个 Maven 目标;
![]() |
- 在 [2] 中:有两个目标:[clean] 用于从 Maven 项目中删除 [target] 文件夹,[package] 用于重新生成该文件夹;
- 在 [3] 中:生成的 [target] 文件夹将创建在此文件夹中;
- 在 [4] 中:目标已生成;
注意:为确保生成成功,STS 使用的 JVM 必须是 JDK [窗口 / 首选项 / Java / 已安装的 JRE]:
![]() |
在控制台显示的日志中,务必确认是否出现了 [spring-boot-maven-plugin] 插件。该插件负责生成可执行归档文件。
使用控制台,导航至生成的文件夹:
gs-serving-web-content-complete\target>dir
...
Répertoire de D:\data\istia-1415\spring mvc\dvp\gs-serving-web-content-complete
\target
27/11/2014 17:07 <DIR> .
27/11/2014 17:07 <DIR> ..
27/11/2014 17:07 <DIR> classes
27/11/2014 17:07 <DIR> generated-sources
27/11/2014 17:07 13 419 551 gs-serving-web-content-0.1.0.jar
27/11/2014 17:07 3 522 gs-serving-web-content-0.1.0.jar.original
27/11/2014 17:07 <DIR> maven-archiver
27/11/2014 17:07 <DIR> maven-status
- 第 12 行:生成的归档文件;
该归档文件的执行方式如下:
gs-serving-web-content-complete\target>java -jar gs-serving-web-content-0.1.0.jar
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v1.1.9.RELEASE)
2014-11-27 17:14:50.439 INFO 8172 --- [ main] hello.Application : Starting Application on Gportpers3 with PID 8172 (D:\data\istia-1415\spring mvc\dvp\gs-serving-web-content-complete\target\gs-serving-web-content-0.1.0.jar started by ST in D:\data\istia-1415\spring mvc\dvp\gs-serving-web-content-complete\target)
2014-11-27 17:14:50.491 INFO 8172 --- [ main] ationConfigEmbeddedWebApplicationContext : Refreshing org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@12f4ec3a: startup date [Thu Nov 27 17:14:50 CET 2014]; root of context hierarchy
注意:您必须先停止在 Eclipse 中可能已启动的任何 Web 服务(参见第 17 页)。
现在 Web 应用程序已运行,您可以使用浏览器访问它:
![]() |
1.6.8. 在 Tomcat 服务器上部署应用程序
虽然 Spring Boot 在开发模式下非常方便,但生产环境中的应用程序将部署在真正的 Tomcat 服务器上。具体操作步骤如下:
按以下方式修改 [pom.xml] 文件:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework</groupId>
<artifactId>gs-serving-web-content</artifactId>
<version>0.1.0</version>
<packaging>war</packaging>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.1.9.RELEASE</version>
</parent>
<dependencies>
<!-- thymeleaf environment -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!-- war generation -->
<!-- <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency> -->
</dependencies>
<properties>
<start-class>hello.Application</start-class>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>spring-milestone</id>
<url>https://repo.spring.io/libs-release</url>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>spring-milestone</id>
<url>https://repo.spring.io/libs-release</url>
</pluginRepository>
</pluginRepositories>
</project>
需要在两个地方进行修改:
- 第 9 行:必须指定要生成 WAR(Web Archive)文件;
- 第 24–28 行:必须添加对 [spring-boot-starter-tomcat] 构建产品的依赖。该构建产品将项目依赖中的所有 Tomcat 类包含在内;
- 第 27 行:该工件的配置为 [provided],这意味着相应的归档文件不会包含在生成的 WAR 文件中。相反,这些归档文件将位于应用程序运行的 Tomcat 服务器上;
事实上,如果查看项目当前的依赖关系,我们会发现 [spring-boot-starter-tomcat] 依赖项已经存在:
![]() |
因此无需将其添加到 [pom.xml] 文件中。我们已将其注释掉以供参考。
我们还需要配置 Web 应用程序。如果没有 [web.xml] 文件,则通过一个继承自 [SpringBootServletInitializer] 的类来完成此配置:
![]() |
[ApplicationInitializer] 类的定义如下:
package hello;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.context.web.SpringBootServletInitializer;
public class ApplicationInitializer extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(Application.class);
}
}
- 第 6 行:[ApplicationInitializer] 类继承自 [SpringBootServletInitializer] 类;
- 第 9 行:重写了 [configure] 方法(第 8 行);
- 第 10 行:提供了用于配置该项目的类;
要运行该项目,请按以下步骤操作:
![]() |
- 在 [1] 中,在 Eclipse IDE 中已注册的某台服务器上运行该项目;
- 在 [2] 中,选择上方的 [Tomcat v8.0];
完成上述操作后,您可以在浏览器中输入 URL [http://localhost:8080/gs-rest-service/greeting/?name=Mitchell]:
![]() |
注意:根据 [Tomcat] 和 [TC Server Developer] 的版本不同,此操作可能会失败。例如,在 [Apache Tomcat 8.0.3 和 8.0.15] 中就曾出现过这种情况。在上例中,使用的 Tomcat 版本为 [8.0.9]。
现在我们已经掌握了生成 WAR 归档文件的方法。接下来,我们将继续学习 Spring Boot 及其可执行 JAR 归档文件。
1.7. 第二个 Spring MVC 项目
1.7.1. 演示项目
![]() |
- 在 [1] 中,我们引入了 Spring 指南之一;
![]() |
- 在 [2] 中,我们选择 [Rest Service] 示例;
- 在 [3] 中,我们选择了 Maven 项目;
- 在 [4] 中,我们选择指南的最终版本;
- 在 [5] 中,我们确认;
- 在 [6] 中,导入项目;
通过标准 URL 访问并返回 JSON 数据的 Web 服务通常被称为 REST(表征状态转移)服务。在本文中,我将把我们要构建的服务简称为 Web/JSON 服务。如果一个服务遵循某些规则,则被称为 RESTful 服务。我并未尝试遵守这些规则。
现在让我们来检查这个导入的项目,首先从它的 Maven 配置开始。
1.7.2. Maven 配置
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework</groupId>
<artifactId>gs-rest-service</artifactId>
<version>0.1.0</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.1.9.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<properties>
<start-class>hello.Application</start-class>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>spring-releases</id>
<url>https://repo.spring.io/libs-release</url>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>spring-releases</id>
<url>https://repo.spring.io/libs-release</url>
</pluginRepository>
</pluginRepositories>
</project>
- 第 6–8 行:Maven 项目属性。缺少一个指定 Maven 构建所生成文件类型的 [<packaging>] 标签。在缺少该标签的情况下,默认使用 [jar] 类型。因此,该应用程序是一个基于控制台的可执行应用程序,而非 Web 应用程序(如果是 Web 应用程序,则应使用 [war] 类型);
- 第 10–14 行:该 Maven 项目有一个父项目 [spring-boot-starter-parent]。它定义了该项目的大部分依赖项。这些依赖项可能已足够(此时不会添加额外依赖),也可能不足(此时会添加缺失的依赖);
- 第 17–20 行:[spring-boot-starter-web] 构建产物包含 Spring MVC Web 服务项目所需的库,该项目不生成视图。该构建产物包含大量库,其中包括用于嵌入式 Tomcat 服务器的库。应用程序将在该服务器上运行;
此配置中包含的库数量众多:
![]() | ![]() |
上图中,我们可以看到三个 Tomcat 服务器压缩包。
1.7.3. Spring [Web / JSON] 服务的架构
让我们回顾一下 Spring MVC 是如何实现 MVC 模型的:
![]() |
客户端请求的处理流程如下:
- 请求 - 请求的 URL 格式为 http://machine:port/contexte/Action/param1/param2/....?p1=v1&p2=v2&... [Dispatcher Servlet] 是 Spring 框架中负责处理传入 URL 的类。它会将 URL “路由”到必须处理该请求的操作(Action)。这些操作是称为 [控制器(Controller)] 的特定类中的方法。 此处的 MVC 中的 C 代表 [Dispatcher Servlet、Controller、Action] 这一链条。如果未配置任何 Action 来处理传入的 URL,[Dispatcher Servlet] 将返回请求的 URL 未找到(404 NOT FOUND 错误);
- 处理
- 选定的 Action 可以使用 [Dispatcher Servlet] 传递给它的参数。这些参数可能来自多个来源:
- URL 的路径 [/param1/param2/...],
- URL 参数 [p1=v1&p2=v2],
- 浏览器随请求提交的参数;
- 在处理用户请求时,操作可能需要调用 [业务] 层 [2b]。一旦处理完客户端的请求,可能会触发各种响应。一个典型的例子是:
- 若请求无法正确处理,则返回错误页面
- 否则则返回确认页面
- 操作会指示特定的视图进行渲染 [3]。该视图将显示被称为视图模型的数据。这就是 MVC 中的 M。操作将创建此 M 模型 [2c] 并指示 V 视图进行渲染 [3];
- 响应——选定的视图 V 使用操作生成的模型 M 来初始化其必须发送给客户端的 HTML 响应中的动态部分,然后发送该响应。
对于 Web 服务 / JSON,上述架构稍作修改:
![]() |
- 在 [4a] 中,模型(即一个 Java 类)通过 JSON 库转换为 JSON 字符串;
- 在 [4b] 中,该 JSON 字符串被发送至浏览器;
1.7.4. C 控制器
![]() |
导入的应用程序包含以下控制器:
package hello;
import java.util.concurrent.atomic.AtomicLong;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class GreetingController {
private static final String template = "Hello, %s!";
private final AtomicLong counter = new AtomicLong();
@RequestMapping("/greeting")
public Greeting greeting(@RequestParam(value = "name", defaultValue = "World") String name) {
return new Greeting(counter.incrementAndGet(), String.format(template, name));
}
}
- 第 9 行:[@RestController] 注解将 [GreetingController] 类定义为 Spring 控制器,这意味着其方法已被注册以处理 URL。我们之前见过类似的 [@Controller] 注解。该控制器方法的返回类型是 [String],即要显示的视图名称。而这里则有所不同。 [@RestController] 的方法返回的对象会被序列化后发送至浏览器。具体的序列化类型取决于 Spring MVC 的配置。在此处,它们将被序列化为 JSON。正是由于项目依赖中包含 JSON 库,才导致 Spring Boot 自动以这种方式配置项目;
- 第 14 行:[@RequestMapping] 注解指定了该方法处理的 URL,本例中为 [/greeting];
- 第 15 行:我们已经解释过 [@RequestParam] 注解。该方法返回的结果是一个 [Greeting] 类型的对象。
- 第 12 行:一个原子类型的长整型。这意味着它支持并发访问。多个线程可能希望同时递增 [counter] 变量。系统将妥善处理这种情况。只有当当前正在修改计数器的线程完成修改后,其他线程才能读取计数器的值。
1.7.5. M 模型
前一种方法生成的 M 模型是以下 [Greeting] 对象:
![]() |
package hello;
public class Greeting {
private final long id;
private final String content;
public Greeting(long id, String content) {
this.id = id;
this.content = content;
}
public long getId() {
return id;
}
public String getContent() {
return content;
}
}
将此对象转换为 JSON 将生成字符串 {"id":n,"content":"text"}。最终,控制器方法生成的 JSON 字符串将采用以下格式:
{"id":2,"content":"Hello, World!"}
或
{"id":2,"content":"Hello, John!"}
1.7.6. 执行
![]() |
[Application.java] 类是该项目的可执行类。其代码如下:
package hello;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.SpringApplication;
import org.springframework.context.annotation.ComponentScan;
@ComponentScan
@EnableAutoConfiguration
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
我们在前面的示例中已经遇到并解释过这段代码。
1.7.7. 运行项目
![]() |
我们得到以下控制台日志:
- 第 13 行:Tomcat 服务器在端口 8080 上启动(第 12 行);
- 第 17 行:存在 [DispatcherServlet] Servlet;
- 第 20 行:已发现方法 [GreetingController.greeting];
要测试该 Web 应用程序,请访问 URL [http://localhost:8080/greeting]:
![]() | ![]() |
我们收到了预期的 JSON 字符串。
注意:此示例在 Eclipse 的内置浏览器中无法正常运行。
查看服务器发送的 HTTP 头部信息可能会有所帮助。为此,我们将使用名为 [Advanced Rest Client] 的 Chrome 插件(参见附录第 9.6 节):
![]() |
- 在[1]中,请求的URL;
- 在 [2] 中,使用了 GET 方法;
- 在 [3] 中,JSON 响应;
- 在 [4] 中,服务器表明其将以 JSON 格式发送响应;
- 在 [5] 中,我们请求相同的 URL,但这次使用 POST 请求;
- 在 [7] 中,信息以 [urlencoded] 格式发送至服务器;
- 在 [6] 中,name 参数及其值;
- 在 [8] 中,浏览器告知服务器将发送 [urlencoded] 数据;
- 在 [9] 中,服务器的 JSON 响应;
1.7.8. 创建可执行归档文件
![]() |
![]() |
- 在 [1] 中:我们运行一个 Maven 目标;
- 在 [2] 中:有两个目标:[clean] 用于从 Maven 项目中删除 [target] 文件夹,[package] 用于重新生成该文件夹;
- 在 [3] 中:生成的 [target] 文件夹将位于此文件夹中;
- 在 [4] 中:我们生成 target;
在控制台显示的日志中,重要的是要看到 [spring-boot-maven-plugin] 插件。这是生成可执行归档文件的插件。
使用终端,导航至生成的文件夹:
D:\Temp\wksSTS\gs-rest-service\target>dir
...
11/06/2014 15:30 <DIR> classes
11/06/2014 15:30 <DIR> generated-sources
11/06/2014 15:30 11 073 572 gs-rest-service-0.1.0.jar
11/06/2014 15:30 3 690 gs-rest-service-0.1.0.jar.original
11/06/2014 15:30 <DIR> maven-archiver
11/06/2014 15:30 <DIR> maven-status
...
- 第 5 行:生成的归档文件;
该归档文件的执行方式如下:
D:\Temp\wksSTS\gs-rest-service-complete\target>java -jar gs-rest-service-0.1.0.jar
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v1.1.0.RELEASE)
2014-06-11 15:32:47.088 INFO 4972 --- [ main] hello.Application
: Starting Application on Gportpers3 with PID 4972 (D:\Temp\wk
sSTS\gs-rest-service-complete\target\gs-rest-service-0.1.0.jar started by ST in
D:\Temp\wksSTS\gs-rest-service-complete\target)
...
注意:您必须先停止 Eclipse 中可能已启动的任何 Web 服务(参见第 1.6.6 节)。
现在 Web 应用程序已运行,您可以通过浏览器访问它:
![]() |
1.7.9. 在 Tomcat 服务器上部署应用程序
与上一个项目一样,我们将 [pom.xml] 文件修改如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework</groupId>
<artifactId>gs-rest-service</artifactId>
<version>0.1.0</version>
<packaging>war</packaging>
...
</project>
- 第 9 行:您必须指定将生成一个 WAR(Web Archive)文件;
你还需要配置 Web 应用程序。如果不存在 [web.xml] 文件,则通过继承 [SpringBootServletInitializer] 的类来完成此配置:
![]() |
[ApplicationInitializer] 类的定义如下:
package hello;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.context.web.SpringBootServletInitializer;
public class ApplicationInitializer extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(Application.class);
}
}
- 第 6 行:[ApplicationInitializer] 类继承自 [SpringBootServletInitializer] 类;
- 第 9 行:重写了 [configure] 方法(第 8 行);
- 第 10 行:提供了用于配置该项目的类;
要运行该项目,请按以下步骤操作:
![]() |
- 在 [1-2] 中,在 Eclipse IDE 中注册的某台服务器上运行该项目;
完成上述操作后,您可以在浏览器中访问 URL [http://localhost:8080/gs-rest-service/greeting/?name=Mitchell]:
![]() |
1.8. 结论
我们介绍了两种类型的 Spring MVC 项目:
- 一种是 Web 应用程序向浏览器发送 HTML 流的项目。该流由 [Thymeleaf] 视图引擎生成;
- Web 应用程序向浏览器发送 JSON 流的项目;
在第一种情况下,该项目需要两个 Maven 依赖项:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.1.9.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
</dependencies>
在第二种情况下,Maven 依赖项如下:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.1.9.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
这些配置引入了大量级联依赖,其中许多是多余的。为了部署应用程序,我们将使用一个手动配置的 Maven 项目,其中仅包含该项目所需的依赖项。
现在,我们将通过介绍两个基本概念,回归 Web 编程的基础:
- 浏览器与 Web 应用程序之间的 HTTP(超文本传输协议)交互;
- 浏览器通过解析 HTML(超文本标记语言)来显示接收到的页面;


















































