1.4 俯瞰Spring风景线

要想了解Spring的整体状况,只需查看完整版本的SpringInitializr Web表单上的那一堆复选框列表即可。它列出了100多个可选的依赖项,所以我不会在这里列出所有选项,也不会提供截图,但我鼓励你去看一看。同时,在这里我会简单介绍一些重点的项目。

1.4.1 Spring核心框架

如你所料,Spring核心框架是Spring领域中一切的基础。它提供了核心容器和依赖注入框架,另外还提供了一些其他重要的特性。

其中有一项是Spring MVC,也就是Spring的Web框架。你已经看到了如何使用Spring MVC来编写控制器类以处理Web请求。但是,你还没看到的是,Spring MVC还能用来创建RESTAPI,以生成非HTML的输出。在第2章中,我们将会更深入地介绍Spring MVC,并在第6章重新学习如何使用它来创建RESTAPI。

Spring核心框架还提供了一些对数据持久化的基础支持,尤其是基于模板的JDBC支持。在第3章中,你将会看到如何使用JdbcTemplate。

在最新版本的Spring中,还添加了对反应式(reactive)风格编程的支持,其中包括名为Spring WebFlux的新反应式Web框架,这个框架大量借鉴了Spring MVC。在第3部分中,我们将会学习Spring反应式编程模型,并在第11章专门学习SpringWebFlux。

1.4.2 Spring Boot

我们已经看到了Spring Boot带来的很多收益,包括starter依赖和自动配置。在本书中,我们会尽可能多地使用Spring Boot,并避免任何形式的显式配置,除非显式配置是绝对必要的。除了starter依赖和自动配置,Spring Boot还提供了大量其他有用的特性:

  • Actuator能够洞察应用运行时的内部工作状况,包括指标、线程dump信息、应用的健康状况以及应用可用的环境属性;
  • 灵活的环境属性规范;
  • 在核心框架的测试辅助功能之上提供了对测试的额外支持。

除此之外,Spring Boot还提供了一个基于Groovy脚本的编程模型,称为Spring Boot命令行接口(Command-LineInterface,CLI)。使用Spring Boot CLI,我们可以将整个应用程序编写为Groovy脚本的集合,并通过命令行运行它们。我们不会花太多时间介绍Spring Boot CLI,但是当它匹配我们的需求时,我们会偶尔提及它。

Spring Boot已经成为Spring开发中不可或缺的一部分,很难想象如果没有它我该如何开发Spring应用程序。因此,本书采用以Spring Boot为核心的视角。当我介绍Spring Boot所做的事情时,你可能会发现我却使用了Spring这个词。

1.4.3 Spring Data

尽管Spring核心框架提供了基本的数据持久化支持,但是Spring Data提供了非常令人惊叹的功能:将应用程序的数据repository定义为简单的Java接口,在定义驱动存储和检索数据的方法时使用一种命名约定即可。

此外,Spring Data能够处理多种不同类型的数据库,包括关系型数据库(JPA)、文档数据库(Mongo)、图数据库(Neo4j)等。在第3章中,我们将使用Spring Data为TacoCloud应用程序创建repository。

1.4.4 Spring Security

应用程序的安全性一直是一个重要的话题,而且正在变得越来越重要。幸运的是,Spring有一个健壮的安全框架,名为Spring Security。

Spring Security解决了应用程序通用的安全性需求,包括身份验证、授权和API安全性。Spring Security的范围太大,在本书中无法得到充分的介绍,但是我们将在第4章和第11章中讨论一些常见的使用场景。

1.4.5 Spring Integration和Spring Batch

从一定程度上来讲,大多数应用程序都需要与其他应用甚至本应用中的其他组件进行集成。在这方面,有一些应用程序集成模式可以解决这些需求。Spring Integration和Spring Batch为基于Spring的应用程序提供了这些模式的实现。

Spring Integration解决了实时集成问题。在实时集成中,数据在可用时马上就会得到处理。相反,Spring Batch解决的则是批处理集成的问题,在此过程中,数据可以收集一段时间,直到某个触发器(可能是一个时间触发器)发出信号,表示该处理批量数据了才会对数据进行批处理。我们将会在第9章中研究Spring Batch和Spring Integration。

1.4.6 Spring Cloud

在撰写本书的时候,应用程序开发领域正在进入一个新的时代,我们不再将应用程序作为单个部署单元来开发,而是使用由微服务组成的多个独立部署单元来组合形成应用程序。

微服务是一个热门话题,解决了开发期和运行期的一些实际问题。然而,在这样做的过程中,它们也面临着自己所带来的挑战。这些挑战将由Spring Cloud直面解决,Spring Cloud是使用Spring开发云原生应用程序的一组项目。

Spring Cloud覆盖了很多领域,本书不可能面面俱到,我们将在第13~15章中研究Spring Cloud的一些常见组件。要更全面地研究Spring Cloud,我建议阅读John Carnell的SpringMicroservices in Action一书^1(Manning,2017)。

1.3 编写Spring应用

因为是刚刚开始,所以我们首先为Taco Cloud做一些小的变更,但是这些变更会展现Spring的很多优点。在刚开始的时候,比较合适的做法是为Taco Cloud应用添加一个主页。在添加主页时,我们将会创建两个代码构件:

  • 一个控制器类,用来处理主页相关的请求;
  • 一个视图模板,用来定义主页看起来是什么样子。

测试是非常重要的,所以我们还会编写一个简单的测试类来测试主页。但是,要事优先,我们需要先编写控制器。

1.3.1 处理Web请求

Spring自带了一个强大的Web框架,名为Spring MVC。Spring MVC的核心是控制器(controller)的理念。控制器是处理请求并以某种方式进行信息响应的类。在面向浏览器的应用中,控制器会填充可选的数据模型并将请求传递给一个视图,以便于生成返回给浏览器的HTML。

在第2章中,我们将会学习更多关于Spring MVC的知识。现在,我们会编写一个简单的控制器类以处理对根路径(比如,“/”)的请求,并将这些请求转发至主页视图,在这个过程中不会填充任何的模型数据。程序清单1.4展示了这个简单的控制器类。

程序清单1.4 主页控制器

1
2
3
4
5
6
7
8
9
10
package tacos;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
@Controller //⇽--- 控制器
public class HomeController {
@GetMapping("/") //⇽--- 处理对根路径“/”的请求
public String home() {
return "home"; //⇽--- 返回视图名
}
}

可以看到,这个类带有@Controller。就其本身而言,@Controller并没有做太多的事情。它的主要目的是让组件扫描将这个类识别为一个组件。因为HomeController带有@Controller,所以Spring的组件扫描功能会自动发现它,并创建一个HomeController实例作为Spring应用上下文中的bean。

实际上,有一些其他的注解与@Controller有着类似的目的(包括@Component、@Service和@Repository)。你可以为HomeController添加上述的任意其他注解,其作用是完全相同的。但是,在这里选择使用@Controller更能描述这个组件在应用中的角色。

home()是一个简单的控制器方法。它带有@GetMapping注解,表明如果针对“/”发送HTTP GET请求,那么这个方法将会处理请求。该方法所做的只是返回String类型的home值。

这个值将会被解析为视图的逻辑名。视图如何实现取决于多个因素,但是因为Thymeleaf位于类路径中,所以我们可以使用Thymeleaf来定义模板。

为何使用Thymeleaf

你可能会想为什么要选择Thymeleaf作为模板引擎呢?为何不使用JSP?为何不使用FreeMarker?为何不选择其他的几个可选方案?

简单来说,我必须要做出选择,我喜欢Thymeleaf,相对于其他的方案,我会优先使用它。即便JSP是更加显而易见的选择,但是组合使用JSP和Spring Boot需要克服一些挑战。我不想脱离第1章的内容定位,所以在这里就此打住。在第2章中,我们将会看一下其他的模板方案,其中也包括JSP。

模板名称是由逻辑视图名派生而来的,再加上“/templates/”前缀和“.html”后缀。最终形成的模板路径将是“/templates/home.html”。所以,我们需要将模板放到项目的“/src/main/resources/templates/home.html”目录中。现在,就让我们来创建这个模板。

1.3.2 定义视图

为了让主页尽可能简单,除了欢迎用户访问站点之外,它不会做其他的任何事情。程序清单1.5展现了基本的Thymeleaf模板,它定义了Taco Cloud的主页。

程序清单1.5 Taco Cloud主页模板

1
2
3
4
5
6
7
8
9
10
11
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org">
<head>
<title>Taco Cloud</title>
</head>
<body>
<h1>Welcome to...</h1>
<img th:src="@{/images/TacoCloud.png}"/>
</body>
</html>

这个模板并没有太多需要讨论的。唯一需要注意的一行代码是用于展现Taco Cloud Logo的<img>标签。它使用了Thymeleaf的th:src属性和@{...}表达式,以便于引用相对于上下文路径的图片。除此之外,它就是一个Hello World页面。

但是,我们再讨论一下这个图片。我将定义Taco Cloud Logo的工作留给你,你需要将它放到应用的正确位置中。

图片是使用相对于上下文的“/images/TacoCloud.png”路径来进行引用的。回忆一下我们的项目结构,像图片这样的静态资源是放到“/src/main/resources/static”文件夹中的。这意味着,在项目中,Taco Cloud Logo图片必须要位于“/src/main/resources/static/ images/TacoCloud.png”。

我们已经有了一个处理主页请求的控制器并且有了渲染主页的模板,现在基本就可以启动应用来看一下它的效果了。在此之前,我们先看一下如何为控制器编写测试。

1.3.3 测试控制器

在测试Web应用时,对HTML页面的内容进行断言是比较困难的。幸好Spring对测试提供了强大的支持,这使得测试Web应用变得非常简单。

对于主页来说,我们所编写的测试在复杂性上与主页本身差不多。测试需要针对根路径“/”发送一个HTTP GET请求并期望得到成功结果,其中视图名称为home并且结果内容包含“Welcome to…”。程序清单1.6能够完成该任务。

程序清单1.6 针对主页控制器的测试

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
package tacos;
import static org.hamcrest.Matchers.containsString;
import static
org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static
org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static
org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static
org.springframework.test.web.servlet.result.MockMvcResultMatchers.view;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
@RunWith(SpringRunner.class)
@WebMvcTest(HomeController.class) //⇽--- 针对HomeController的Web测试
public class HomeControllerTest {
@Autowired
private MockMvc mockMvc; //⇽--- 注入MockMvc
@Test
public void testHomePage() throws Exception {
mockMvc.perform(get("/")) //⇽--- 发起对“/”的GET
.andExpect(status().isOk()) //⇽--- 期望得到HTTP 200
.andExpect(view().name("home")) //⇽--- 期望得到home视图
.andExpect(content().string( //⇽--- 期望包含“Welcome to...”
containsString("Welcome to...")));
}

对于这个测试,我们首先注意到的可能就是它使用了与TacoCloudApplicationTests类不同的注解。HomeControllerTest没有使用@SpringBootTest标记,而是添加了@WebMvcTest注解。这是Spring Boot所提供的一个特殊测试注解,它会让这个测试在Spring MVC应用的上下文中执行。更具体来讲,在本例中,它会将HomeController注册到Spring MVC中,这样的话,我们就可以向它发送请求了。

@WebMvcTest同样会为测试Spring MVC应用提供Spring环境的支持。尽管我们可以启动一个服务器来进行测试,但是对于我们的场景来说,仿造一下Spring MVC的运行机制就可以。测试类被注入了一个MockMvc,能够让测试实现mockup。

通过testHomePage()方法,我们定义了针对主页想要执行的测试。它首先使用MockMvc对象对“/”(根路径)发起HTTPGET请求。对于这个请求,我们设置了如下的预期:

  • 响应应该具备HTTP 200 (OK)状态;
  • 视图的逻辑名称应该是home;
  • 渲染后的视图应该包含文本“Welcome to…”。

如果在MockMvc对象发送请求之后,这些期望有不满足的话,那么这个测试会失败。但是,我们的控制器和模板引擎在编写时都满足了这些预期,所以测试应该能够通过,并且带有成功的图标——至少能够看到一些绿色的背景,表明测试通过了。

控制器已经编写好了,视图模板也已经创建完毕,而且我们还通过了测试,看上去我们已经成功实现了主页。尽管测试已经通过了,但是如果能够在浏览器中看到结果那会更有成就感,毕竟这才是Taco Cloud的客户所能看到的效果。接下来,我们构建应用并运行它。

1.3.4 构建和运行应用

就像初始化Spring应用有多种方式一样,运行Spring应用也有多种方式。如果你愿意的话,可以翻到附录部分,以了解运行Spring Boot应用的一些通用方式。

因为我们选择了使用Spring Tool Suite来初始化和处理项目,所以可以借助名为Spring Boot Dashboard的便捷功能来帮助我们在IDE中运行应用。Spring Boot Dashboard的表现形式是一个Tab标签,通常会位于IDE窗口的左下角附近。图1.7展现了一个带有标注的Spring Boot Dashboard截屏。

epub_29101559_14

图1.7 Spring Boot Dashboard的重点功能

图1.7包含了一些最有用的细节,但是我不想花太多时间介绍Spring Boot Dashboard支持的所有功能。对我们来说,现在最重要的事情是需要知道如何使用它来运行TacoCloud应用。确保taco-cloud应用程序在项目列表中能够显示出来(这是图1.7中显示的唯一应用),然后点击启动按钮(上方工具栏最左边的按钮,也就是带有绿色三角形和红色正方形的按钮),应用程序应该就能立即启动。

在应用启动的过程中,你会在控制台看到一些Spring ASCII码,随后会是描述应用启动各个步骤的日志条目。在控制台输出的最后,你将会看到一条Tomcat已经在port(s): 8080 (http)启动的日志,这意味着此时你可以打开Web浏览器并导航至主页,这样就能看到我们的劳动成果了。

稍等一下!刚才说启动Tomcat?但是我们是什么时候将应用部署到Tomcat的呢?

Spring Boot应用的习惯做法是将所有它需要的东西都放到一起,没有必要将其部署到某种应用服务器中。在这个过程中,我们根本没有将应用部署到Tomcat中……Tomcat是我们应用的一部分!(在1.3.6小节,我会介绍Tomcat是如何成为我们应用的一部分的。)

现在,应用已经启动起来了,打开Web浏览器并访问http://localhost:8080(或者在Spring Boot Dashboard中点击上方的地球样式的按钮,如图1.7所示),你将会看到如图1.8所示的界面。如果你设计了自己的Logo图片,那么显示效果可能会有所不同。但是,与图1.8相比,应该不会有太大的差异。

epub_29101559_15

图1.8 Taco Cloud主页

看上去似乎并不太美观,但这不是一本关于平面设计的书。目前,略显简陋的主页外观已经足够了,它为我们学习Spring打下了一个良好的开端。

到现在为止,我一直没有提及DevTools。在初始化项目的时候,我们将其作为一个依赖添加了进来。在最终生成的pom.xml文件中,它表现为一个依赖项。甚至Spring BootDashboard都显示项目启用了DevTools。那么,DevTools是什么,它又能为我们做些什么呢?接下来,让我们快速浏览一下DevTools最有用的一些特性。

1.3.5 了解Spring Boot DevTools

顾名思义,DevTools为Spring开发人员提供了一些便利的开发期工具,其中包括:

  • 代码变更后应用会自动重启;
  • 当面向浏览器的资源(如模板、JavaScript、样式表)等发生变化时,会自动刷新浏览器;
  • 自动禁用模板缓存;
  • 如果使用H2数据库的话,内置了H2控制台。

需要注意,DevTools并不是IDE插件,它也不需要你使用特定的IDE。在Spring Tool Suite、IntelliJ IDEA和NetBeans中,它都能很好地运行。另外,因为它的目的是仅仅用于开发,所以能够很智能地在生产环境中把自己禁用掉。(我们将会在第19章学习应用部署的时候再讨论它是如何做到这一点的。)现在,我们主要关注Spring Boot DevTools最有用的特性,先从应用的自动重启开始。

应用自动重启

如果将DevTools作为项目的一部分,那么你可以看到,当对项目中的Java代码和属性文件做出修改后,这些变更稍后就能发挥作用。DevTools会监控变更,当它看到有变化的时候,将会自动重启应用。

更准确地说,当DevTools运行的时候,应用程序会被加载到Java虚拟机(Java virtual Machine,JVM)两个独立的类加载器中。其中一个类加载器会加载你的Java代码、属性文件以及项目中“src/main/”路径下几乎所有的内容。这些条目很可能会经常发生变化。另外一个类加载器会加载依赖的库,这些库不太可能经常发生变化。

当探测到变更的时候,DevTools只会重新加载包含项目代码的类加载器,并重启Spring的应用上下文,在这个过程中另外一个类加载器和JVM会原封不动。这个策略非常精细,但是它能减少应用启动的时间。

这种策略的一个不足之处就是自动重启无法反映依赖项的变化。这是因为包含依赖库的类加载器不会自动重新加载。这意味着每当我们在构建规范中添加、变更或移除依赖的时候,为了让变更生效,我们需要重新启动应用。

浏览器自动刷新和禁用模板缓存

默认情况下,像Thymeleaf和FreeMarker这样的模板方案在配置时会缓存模板解析的结果。这样的话,在为每个请求提供服务的时候,模板就不用重新解析了。在生产环境中,这是一种很好的方式,因为它会带来一定的性能收益。

但是,在开发期,缓存模板就不太好了。在应用运行的时候,如果缓存模板,那么我们刷新浏览器就无法看到模板变更的效果了。即便我们对模板做了修改,在应用重启之前,缓存的模板依然会有效。

DevTools通过禁用所有模板缓存解决了这个问题。你可以对模板进行任意数量的修改,只需要刷新一下浏览器就能看到结果。

如果你像我这样,连浏览器的刷新按钮都懒得点,那么对代码做出变更之后,马上在浏览器中看到结果就好了。幸运的是,DevTools有一些特殊的功能可以供我们使用。

DevTools在运行的时候,它会和你的应用程序一起,同时自动启动一个LiveReload服务器。LiveReload服务器本身并没有太大的用处。但是,当它与LiveReload浏览器插件结合起来的时候,就能够在模板、图片、样式表、JavaScript等(实际上,几乎涵盖为浏览器提供服务的所有内容)发生变化的时候自动刷新浏览器。

LiveReload有针对Google Chrome、Safari和Firefox的浏览器插件(要对Internet Explorer和Edge粉丝说声抱歉)。请访问LiveReload官网,以了解如何为你的浏览器安装LiveReload。

内置的H2控制台

虽然我们的项目还没有使用数据库,但是这种情况在第3章中就会发生变化。如果你使用H2数据库进行开发,DevTools将会自动启用H2。这样的话,我们可以通过Web浏览器进行访问。你只需要让浏览器访问http://localhost:8080/h2-console,就能看到应用所使用的数据。

此时,我们已经编写了一个尽管非常简单却很完整的Spring应用。在本书中,我们将会不断扩展它。现在,我们要回过头来看一下都完成了哪些工作以及Spring发挥了什么作用。

1.3.6 回顾一下

回想一下我们是怎样完成这一切的。简短来说,在构建基于Spring的Taco Cloud应用的过程中,我们执行了如下步骤:

  • 使用Spring Initializr创建初始的项目结构;
  • 编写控制器类处理针对主页的请求;
  • 定义了一个视图模板来渲染主页;
  • 编写了一个简单的测试类来验证工作符合预期。

这些步骤都非常简单直接,对吧?除了初始化应用的第一个步骤之外,我们所做的每一个操作都专注于生成主页的目标。

实际上,我们所编写的每行代码都致力于实现这个目标。除了Java import语句之外,我只能在控制器中找到两行Spring相关的代码,而在视图模板中一行Spring相关的代码都没有。尽管测试类的大部分内容都使用了Spring对测试的支持,但是它在测试的上下文中似乎没有那么具有侵入性。

这是使用Spring进行开发的一个重要收益。你可以只关注满足应用需求的代码,无须考虑如何满足框架的需求。尽管我们偶尔还是需要编写一些框架特定的代码,但是它们通常只占整个代码库很小的一部分。正如我在前文所述,Spring(以及Spring Boot)可以视为感受不到框架的框架(frameworklessframework)。

但是这又是如何运行起来的呢?Spring在幕后做了些什么来保证应用的需求能够得到满足呢?要理解Spring到底做了些什么,我们首先来看一下构建规范。

在pom.xml文件中,我们声明了对Web和Thymeleaf starter的依赖。这两项依赖会传递引入大量其他的依赖,包括:

  • Spring的MVC框架;
  • 嵌入式的Tomcat;
  • Thymeleaf和Thymeleaf布局方言;

它还引入了Spring Boot的自动配置库。当应用启动的时候,Spring Boot的自动配置将会探测到这些库,并自动完成如下功能:

  • 在Spring应用上下文中配置bean以启用Spring MVC;
  • 在Spring应用上下文中配置嵌入式的Tomcat服务器;
  • 配置Thymeleaf视图解析器,以便于使用Thymeleaf模板渲染Spring MVC视图。

简而言之,自动配置功能完成了所有的脏活累活,让我们能够集中精力编写实现应用功能的代码。如果你问我对此的观点,那么我认为这是一个很好的安排!

我们的Spring之旅才刚刚开始。Taco Cloud应用程序只涉及Spring所提供功能的一小部分。在开始下一步之前,我们先整体了解一下Spring,看看在我们的路途中都会有哪些地标。

1.2 初始化Spring应用

在本书中,我们将会创建一个名为Taco Cloud的在线应用,它能够订购人类所发明的一种美味,也就是墨西哥煎玉米卷(taco)^1。当然,在这个过程中,为了达成我们的目标,我们将会用到Spring、Spring Boot以及各种相关的库和框架。

我们有多种初始化Spring应用的可选方案。尽管我可以教你手动创建项目目录结构和定义构建规范的各个步骤,但这无疑是浪费时间,我们最好将时间花在编写应用代码上。因此,我们将会学习如何使用Spring Initializr初始化应用。

Spring Initializr是一个基于浏览器的Web应用,同时也是一个REST API,能够生成一个Spring项目结构的骨架,我们还可以使用各种想要的功能来填充它。使用Spring Initializr的几种方式如下:

  • 通过地址为 https://start.spring.io/ 的Web应用;
  • 在命令行中使用curl命令;
  • 在命令行中使用Spring Boot命令行接口;
  • 在Spring Tool Suite中创建新项目;
  • 在IntelliJ IDEA中创建新项目;
  • 在NetBeans中创建新项目。

我将这些细节放到了附录中,这样就不用在这里花费很多页的篇幅介绍每种方案了。在本章和本书中,我都会向你展示如何使用我最钟爱的方式创建新项目:在Spring Tool Suite中使用Spring Initializr。

顾名思义,Spring Tool Suite是一个非常棒的Spring开发环境。它同时还提供了便利的Spring Boot Dashboard特性,这个特性是其他IDE都不具备的(至少在我编写本书的时候如此)。

如果你不是Spring Tool Suite用户,那也没有关系,我们依然可以做朋友。你可以跳转到附录中,查看最适合你的Initializr方案,以此来替换后面小节中的内容。但是,在本书中,我偶尔会提到Spring Tool Suite特有的特性,比如Spring BootDashboard。如果你不使用Spring Tool Suite,那么需要调整这些指令以适配你的IDE。

1.2.1 使用Spring Tool Suite初始化Spring项目

要在Spring Tool Suite中初始化一个新的Spring项目,我们首先要点击File菜单,选择New,接下来选择Spring StarterProject。图1.2展现了要查找的菜单结构。

epub_29101559_9

图1.2 在Spring Tool Suite中使用Initializr初始化一个新项目

在选择Spring Starter Project之后,将会出现一个新的向导对话框(见图1.3)。向导的第一页会询问一些项目的通用信息,比如项目名称、描述和其他必要的信息。如果你熟悉Mavenpom.xml文件的内容,就可以识别出大多数的输入域条目最终都会成为Maven的构建规范。对于Taco Cloud应用来说,我们可以按照图1.3的样子来填充对话框。

epub_29101559_10

图1.3 为Taco Cloud应用指定通用的项目信息

向导的下一页会让我们选择要添加到项目中的依赖(见图1.4)。注意,在对话框的顶部,我们可以选择项目要基于哪个Spring Boot版本。它的默认值是最新的可用版本。一般情况下,最好使用这个默认的值,除非你需要使用不同的版本。

至于依赖项本身,你可以打开各个区域并查找所需的依赖项,也可以在Available顶部的搜索框中对依赖进行搜索。对于TacoCloud应用来说,我们最初的依赖项如图1.4所示。

epub_29101559_11

图1.4 选择Starter依赖

现在,你可以点击Finish来生成项目并将其添加到工作空间中。但是,如果你还想多体验一些,那么可以再次点击Next,看一下新Starter项目向导的最后一页,如图1.5所示。

epub_29101559_12

图1.5 指定备用的Initializr地址

默认情况下,新项目的向导会调用Spring Initializr来生成项目。通常情况下,没有必要覆盖默认值,这也是我们可以在向导的第二页直接点击Finish的原因。但是,如果你基于某种原因托管了自己的Initializr克隆版本(可能是本地机器上的副本或者公司防火墙内部运行的自定义克隆版本),那么你可能需要在点击Finish之前修改Base Url输入域,使其指向自己的Initializr实例。

在点击Finish之后,项目会从Initializr下载并加载到工作空间中。此时,要等待它加载和构建,然后你就可以开始开发应用功能了。下面我们看一下Initializr都为我们提供了什么。

1.2.2 检查Spring项目的结构

项目加载到IDE中之后,我们将其展开,看一下其中都包含什么内容。图1.6展现了Spring Tool Suite中已展开的Taco Cloud项目。

epub_29101559_13

图1.6 Spring Tool Suite中所展现的初始Spring项目结构

你可能已经看出来了,这就是一个典型的Maven或Gradle项目结构,其中应用的源码放到了“src/main/java”中,测试代码放到了“src/test/java”中,而非Java的资源放到了“src/main/resources”。在这个项目结构中,我们需要注意以下几点。

  • mvnw和mvnw.cmd:这是Maven包装器(wrapper)脚本。借助这些脚本,即便你的机器上没有安装Maven,也可以构建项目。
  • pom.xml:这是Maven构建规范,随后我们将会深入介绍该文件。
  • TacoCloudApplication.java:这是Spring Boot主类,它会启动该项目。随后,我们会详细介绍这个类。
  • application.properties:这个文件起初是空的,但是它为我们提供了指定配置属性的地方。在本章中,我们会稍微修改一下这个文件,但是我会将配置属性的详细阐述放到第5章。
  • static:在这个文件夹下,你可以存放任意为浏览器提供服务的静态内容(图片、样式表、JavaScript等),该文件夹初始为空。
  • templates:这个文件夹中存放用来渲染内容到浏览器的模板文件。这个文件夹初始是空的,不过我们很快就会往里面添加Thymeleaf模板。
  • TacoCloudApplicationTests.java:这是一个简单的测试类,它能确保Spring应用上下文可以成功加载。在开发应用的过程中,我们会将更多的测试添加进来。

随着Taco Cloud应用功能的增长,我们会不断使用Java代码、图片、样式表、测试以及其他附属内容来充实这个项目结构。不过,在此之前,我们先看一下Spring Initializr提供的几个条目。

探索构建规范

在填充Initializr表单的时候,我们声明项目要使用Maven来进行构建。因此,Spring Initializr所生成的pom.xml文件已经包含了我们所选择的依赖。程序清单1.1展示了Initializr为我们提供的完整pom.xml。

程序清单1.1 初始的Maven构建规范

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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
<?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>sia</groupId>
<artifactId>taco-cloud</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging> ⇽--- 打包为JAR
<name>taco-cloud</name>
<description>Taco Cloud Example</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.4.RELEASE</version> ⇽--- Spring Boot的版本
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>
UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>
UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency> ⇽--- Starter依赖
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>htmlunit-driver</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin> ⇽--- Spring Boot插件
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

在pom.xml文件中,我们第一个需要注意的地方就是<packaging>。我们选择了将应用构建成一个可执行的JAR文件,而不是WAR文件。这可能是你所做出的最奇怪的选择之一,对Web应用来说尤为如此。毕竟,传统的Java Web应用都是打包成WAR文件,JAR只是用来打包库和较为少见的桌面UI应用的。

打包为JAR文件是基于云思维做出的选择。尽管WAR文件非常适合部署到传统的Java应用服务器上,但对于大多数云平台来说它们并不是理想的选择。有些云平台(比如CloudFoundry)也能够部署和运行WAR文件,但是所有的Java云平台都能够运行可执行的JAR文件。因此,Spring Initializr默认会使用基于JAR的打包方式,除非我们明确告诉它采用其他的方式。

如果你想要将应用部署到传统的Java应用服务器上,那么需要选择使用基于WAR的打包方式并要包含一个Web初始化类。在第2章中,我们将会更详细地了解如何构建WAR文件。

接下来,请留意<parent>元素,更具体来说是它的<version>子元素。这表明我们的项目要以spring-boot-starter-parent作为其父POM。除了其他的一些功能之外,这个父POM为Spring项目常用的一些库提供了依赖管理,现在你不需要指定它们的版本,因为这是通过父POM来管理的。这里的2.0.4.RELEASE表明要使用Spring Boot 2.0.4,所以会根据这个版本的Spring Boot定义来继承依赖管理。

既然我们谈到了依赖的话题,那么需要注意在<dependencies>元素下声明了3个依赖。在某种程度上,你可能会对前两个更熟悉一些。它们直接对应我们在Spring ToolSuite新项目向导中点击Finish之前所选择的Web和Thymeleaf依赖。第三个依赖提供了很多有用的测试功能。我们没有必要在专门的复选框中选择它,因为Spring Initializr假定你将会编写测试(希望你会正确地开展这项工作)。

你可能也会注意到这3个依赖的artifact ID上都有starter这个单词。Spring Boot starter依赖的特别之处在于它们本身并不包含库代码,而是传递性地拉取其他的库。这种starter依赖主要有3个好处。

  • 构建文件会显著减小并且更易于管理,因为这样不必为每个所需的依赖库都声明依赖。
  • 我们能够根据它们所提供的功能来思考依赖,而不是根据库的名称。如果是开发Web应用,那么你只需要添加webstarter就可以了,而不必添加一堆单独的库再编写Web应用。
  • 我们不必再担心库版本的问题。你可以直接相信给定版本的Spring Boot,传递性引入的库的版本是兼容的。现在,你只需要关心使用的是哪个版本的Spring Boot就可以了。

最后,构建规范还包含一个Spring Boot插件。这个插件提供了一些重要的功能。

  • 它提供了一个Maven goal,允许我们使用Maven来运行应用。在1.3.4小节,我们将会尝试这个goal。
  • 它会确保依赖的所有库都会包含在可执行JAR文件中,并且能够保证它们在运行时类路径下是可用的。
  • 它会在JAR中生成一个manifest文件,将引导类(在我们的场景中,也就是TacoCloudApplication)声明为可执行JAR的主类。

谈到了主类,我们打开它看一下。

引导应用

因为我们将会通过可执行JAR文件的形式来运行应用,所以很重要的一点就是要有一个主类,它将会在JAR运行的时候被执行。我们同时还需要一个最小化的Spring配置,以引导该应用。这就是TacoCloudApplication类所做的事情,如程序清单1.2所示。

程序清单1.2 Taco Cloud的引导类

1
2
3
4
5
6
7
8
9
package tacos;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication //⇽--- Spring Boot应用
public class TacoCloudApplication {
public static void main(String[] args) {
SpringApplication.run(TacoCloudApplication.class, args); //⇽--- 运行应用
}
}

尽管在TacoCloudApplication中只有很少的代码,但是它包含了很多的内容。其中,最强大的一行代码也是最短的。@SpringBootApplication注解明确表明这是一个Spring Boot应用。但是,@SpringBootApplication远比看上去更强大。

@SpringBootApplication是一个组合注解,它组合了3个其他的注解。

  • @SpringBootConfiguration:将该类声明为配置类。尽管这个类目前还没有太多的配置,但是后续我们可以按需添加基于Java的Spring框架配置。这个注解实际上是@Configuration注解的特殊形式。
  • @EnableAutoConfiguration:启用Spring Boot的自动配置。我们随后会介绍自动配置的更多功能。就现在来说,我们只需要知道这个注解会告诉Spring Boot自动配置它认为我们会用到的组件。
  • @ComponentScan:启用组件扫描。这样我们能够通过像@Component、@Controller、@Service这样的注解声明其他类,Spring会自动发现它们并将它们注册为Spring应用上下文中的组件。

TacoCloudApplication另外一个很重要的地方是它的main()方法。这是JAR文件执行的时候要运行的方法。在大多数情况下,这个方法都是样板代码,我们编写的每个Spring Boot应用都会有一个类似或完全相同的方法(类名不同则另当别论)。

这个main()方法会调用SpringApplication中静态的run()方法,后者会真正执行应用的引导过程,也就是创建Spring的应用上下文。在传递给run()的两个参数中,一个是配置类,另一个是命令行参数。尽管传递给run()的配置类不一定要和引导类相同,但这是最便利和最典型的做法。

你可能并不需要修改引导类中的任何内容。对于简单的应用程序来说,你可能会发现在引导类中配置一两个组件是非常方便的,但是对于大多数应用来说,最好还是要为没有实现自动配置的功能创建一个单独的配置类。在本书的整个过程中,我们将会创建多个配置类,所以请继续关注后续的细节。

测试应用

测试是软件开发的重要组成部分。鉴于此,Spring Initializr为我们提供了一个测试类作为起步。程序清单1.3展现了这个测试类的概况。

程序清单1.3 应用测试类的概况

1
2
3
4
5
6
7
8
9
10
11
12
package tacos;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class) //⇽--- 使用Spring的运行器
@SpringBootTest //⇽--- Spring Boot测试
public class TacoCloudApplicationTests {
@Test //⇽--- 测试方法
public void contextLoads() {
}
}

TacoCloudApplicationTests类中的内容并不多:这个类中只有一个空的测试方法。即便如此,这个测试类还是会执行必要的检查,确保Spring应用上下文能够成功加载。如果你所做的变更导致Spring应用上下文无法创建,那么这个测试将会失败,你就可以做出反应来解决相关的问题了。

TacoCloudApplicationTests类中的内容并不多:这个类中只有一个空的测试方法。即便如此,这个测试类还是会执行必要的检查,确保Spring应用上下文能够成功加载。如果你所做的变更导致Spring应用上下文无法创建,那么这个测试将会失败,你就可以做出反应来解决相关的问题了。

另外,注意这个类带有@RunWith(SpringRunner.class)注解。@RunWith是JUnit的注解,它会提供一个测试运行器(runner)来指导JUnit如何运行测试。可以将其想象为给JUnit应用一个插件,以提供自定义的测试行为。在本例中,为JUnit提供的是SpringRunner,这是一个Spring提供的测试运行器,它会创建测试运行所需的Spring应用上下文。

测试运行器的其他名称

如果你已经熟悉如何编写Spring测试或者见过其他一些基于Spring的测试类,那么你可能见过名为SpringJUnit4ClassRunner的测试运行器。SpringRunner是SpringJUnit4ClassRunner的别名,是在Spring 4.3中引入的,以便于移除对特定JUnit版本的关联(比如,JUnit 4)。毫无疑问,这个别名更易于阅读和输入。

@SpringBootTest会告诉JUnit在启动测试的时候要添加上Spring Boot的功能。从现在开始,我们可以将这个测试类视同为在main()方法中调用SpringApplication.run()。在这本书中,我们将会多次看到@SpringBootTest,而且会不断见识它的威力。

最后,就是测试方法本身了。尽管@RunWith(SpringRunner.class)和@SpringBootTest会为测试加载Spring应用上下文,但是如果没有任何测试方法,那么它们其实什么事情都没有做。即便没有任何断言或代码,这个空的测试方法也会提示这两个注解完成了它们的工作并成功加载Spring应用上下文。如果这个过程中有任何问题,那么测试都会失败。

此时,我们已经看完了Spring Initializr为我们提供的代码。我们看到了一些用来开发Spring应用程序的基础样板,但是还没有编写任何代码。现在是时候启动IDE、准备好键盘并向TacoCloud应用程序添加一些自定义的代码了。

第1章 Spring起步


本章内容:

  • Spring和Spring Boot的必备知识
  • 初始化Spring项目
  • Spring生态系统概览

尽管希腊哲学家赫拉克利特(Heraclitus)并不作为一名软件开发人员而闻名,但他似乎深谙此道。他的一句话经常被引用:“唯一不变的就是变化”,这句话抓住了软件开发的真谛。

我们现在开发应用的方式和1年前、5年前、10年前都是不同的,更别提15年前了,当时Rod Johnson的图书Expert One-on-One J2EE Design and Development介绍了Spring框架的初始形态。

当时,最常见的应用形式是基于浏览器的Web应用,后端由关系型数据库作为支撑。尽管这种形式的开发依然有它的价值,Spring也为这种应用提供了良好的支持,但是我们现在感兴趣的还包括如何开发面向云的由微服务组成的应用,这些应用会将数据保存到各种类型的数据库中。另外一个崭新的关注点是反应式编程,它致力于通过非阻塞操作提供更好的扩展性并提升性能。

随着软件开发的发展,Spring框架也在不断变化,以解决现代应用开发中的问题,其中就包括微服务和反应式编程。Spring还通过引入Spring Boot简化自己的开发模型。

不管你是开发以数据库作为支撑的简单Web应用,还是围绕微服务构建一个现代应用,Spring框架都能帮助你达成目标。本章是使用Spring进行现代应用开发的第一步。

资源与支持

本书由异步社区出品,社区(https://www.epubit.com/)为您提供相关资源和后续服务。

配套资源
本书提供如下资源:

  • 本书源代码;
  • 书中彩图文件。

要获得以上配套资源,请在异步社区本书页面中点击image-20211003192418824,跳转到下载界面,按提示进行操作即可。注意:为保证购书读者的权益,该操作会给出相关提示,要求输入提取码进行验证。

如果您是教师,希望获得教学配套资源,请在社区本书页面中直接联系本书的责任编辑。

提交勘误

作者和编辑尽最大努力来确保书中内容的准确性,但难免会存在疏漏。欢迎您将发现的问题反馈给我们,帮助我们提升图书的质量。

当您发现错误时,请登录异步社区,按书名搜索,进入本书页面,点击“提交勘误”,输入勘误信息,点击“提交”按钮即可。本书的作者和编辑会对您提交的勘误进行审核,确认并接受后,您将获赠异步社区的100积分。积分可用于在异步社区兑换优惠券、样书或奖品。

image-20211003192607753

与我们联系

我们的联系邮箱是contact@epubit.com.cn

如果您对本书有任何疑问或建议,请您发邮件给我们,并请在邮件标题中注明本书书名,以便我们更高效地做出反馈。

如果您有兴趣出版图书、录制教学视频,或者参与图书翻译、技术审校等工作,可以发邮件给我们;有意出版图书的作者也可以到异步社区在线提交投稿(直接访问www.epubit.com/selfpublish/submission即可)。

如果学校、培训机构或企业想批量购买本书或异步社区出版的其他图书,也可以发邮件给我们。

如果您在网上发现有针对异步社区出品图书的各种形式的盗版行为,包括对图书全部或部分内容的非授权传播,请您将怀疑有侵权行为的链接发邮件给我们。您的这一举动是对作者权益的保护,也是我们持续为您提供有价值的内容的动力之源。

关于异步社区和异步图书

“异步社区”是人民邮电出版社旗下IT专业图书社区,致力于出版精品IT技术图书和相关学习产品,为作译者提供优质出版服务。异步社区创办于2015年8月,提供大量精品IT技术图书和电子书,以及高品质技术文章和视频课程。更多详情请访问异步社区官网https://www.epubit.com。

“异步图书”是由异步社区编辑团队策划出版的精品IT专业图书的品牌,依托于人民邮电出版社近30年的计算机图书出版积累和专业编辑团队,相关图书在封面上印有异步图书的LOGO。异步图书的出版领域包括软件开发、大数据、AI、测试、前端、网络技术等。

image-20211003192809100

异步社区

image-20211003192853222

微信服务号

前言

在使用了Spring 15年并编写了这本书的5个版本(暂时不算《Spring Boot实战》了)之后,你可能会认为,在为这本书撰写前言时,我很难想出一些关于Spring令人兴奋的新内容,但事实远非如此!

在Spring生态系统中,Spring、Spring Boot和所有其他项目的每个版本都发布了令人兴奋的新功能,重新点燃了开发应用程序的乐趣。Spring 5.0和Spring Boot 2.0的发布达到了一个重要的里程碑。Spring有了更多的乐趣,所以编写新版《Spring实战》是很容易的。

Spring 5的主要功能是对反应式编程的支持,包括SpringWebFlux。这是一个全新的反应式Web框架,借鉴了SpringMVC的编程模型,允许开发人员创建伸缩性更好且耗用更少线程的Web应用程序。至于Spring应用的后端,最新版本的Spring Data支持创建反应式、非阻塞的数据repository。所有这些都构建在Reactor项目之上,Reactor是一个用于处理反应式类型的Java库。

除了Spring 5新的反应式编程特性之外,Spring Boot 2提供了比以前更多的自动配置支持,以及一个完全重新设计的Actuator,用于探查和操作正在运行的应用。

更重要的是,当开发人员希望将单体应用拆分为分散的微服务时,Spring Cloud提供了一些工具,使配置和发现微服务变得容易,并增强了微服务的功能,使它们更能抵御失败。

我很高兴地说,《Spring实战(第5版)》涵盖了所有的这些功能,甚至更多!如果你是经验丰富的老手,《Spring实战(第5版)》可以作为指南,指导你去学习Spring提供的新功能;如果你是Spring新手,那么现在是行动起来的最佳时机,本书的前几章会让你快速上手!

与Spring合作的15年是令人兴奋的。现在我已经写了5个版本的《Spring实战》,我很想和你们分享这份兴奋!

致谢

Spring和Spring Boot所做的最令人惊奇的事情之一就是自动为应用程序提供所有的基础功能,让开发人员专注于应用程序特有的逻辑。不幸的是,对于写书这件事来说,并没有这样的魔法。是这样的吗?

在Manning,有很多人在施展“魔法”,确保这本书是最好的。特别要感谢我的项目编辑Jenny Stout以及制作团队,其中包括项目主管Janet Vail、文字编辑Andy Carroll和FrancesBuran,以及校对Katie Tennant和Melody Dolab。同时也要感谢技术校对Joshua White,他的工作很全面,很有帮助。

在此过程中,我们得到了几位同行评论的反馈,他们确保了这本书没有偏离目标,涵盖了正确的内容。为此,我要感谢Andrea Barisone、Arnaldo Ayala、Bill Fly、Colin Joyce、Daniel Vaughan、David Witherspoon、Eddu Melendez、Iain Campbell、Jettro Coenradie、John Gunvaldson、Markus Matzker、Nick Rakochy、Nusry Firdousi、PiotrKafel、Raphael Villela、Riccardo Noviello、SergioFernandez Gonzalez、Sergiy Pylypets、Thiago Presa、Thorsten Weber、Waldemar Modzelewski、Yagiz Erkan和Zeljko Trogrlić。

和往常一样,如果不是Spring工程团队成员所做的出色工作,写这本书是没有任何意义的。我惊叹于你们所创造的成果,并期待未来继续改变软件开发的方式。

非常感谢我的同行们在No Fluff/Just Stuff巡回演讲上的发言。我从你们每个人身上学到很多。我特别要感谢Brian Sletten、Nate Schutta和Ken Kousen关于Spring的对话和邮件,这些内容塑造了这本书。

我要再次感谢Phoenicians,你们太棒了[^1]。

最后,我要感谢我美丽的妻子Raymie。她是我生命中的挚爱,是我最甜蜜的梦想,也是我的灵感来源:谢谢你的鼓励,也谢谢你为这本新书做的努力。致我可爱的女儿Maisy和Madi:我为你们感到骄傲,为你们即将成为了不起的年轻女士感到骄傲。我对你们的爱超出了你们的想象,也超出了我语言所能表达的程度。

[^1]:Phoenicians指的是远古时代的腓尼基人,他们被认为是字母系统的创建者,基于字母的所有现代语言都是由此衍生而来的。在迪士尼世界的Epcot,有名为Spaceship Earth的时光穿梭体验,我们可以了解到人类交流的历史,甚至能够回到腓尼基人的时代,在这段旅程的旁白中这样说道:如果你觉得学习字母语言很容易,就感谢腓尼基人吧,是他们发明了它。这是作者的一种幽默说法。——译者注

1.1 什么是Spring

我知道你现在可能迫不及待地想要开始编写Spring应用了,我可以向你保证,在本章结束之前,你肯定能够开发一个简单的Spring应用。首先,我将使用Spring的一些基础概念为你搭建一个舞台,帮助你理解Spring是如何运行起来的。

我知道你现在可能迫不及待地想要开始编写Spring应用了,我可以向你保证,在本章结束之前,你肯定能够开发一个简单的Spring应用。首先,我将使用Spring的一些基础概念为你搭建一个舞台,帮助你理解Spring是如何运行起来的。

我知道你现在可能迫不及待地想要开始编写Spring应用了,我可以向你保证,在本章结束之前,你肯定能够开发一个简单的Spring应用。首先,我将使用Spring的一些基础概念为你搭建一个舞台,帮助你理解Spring是如何运行起来的。

我知道你现在可能迫不及待地想要开始编写Spring应用了,我可以向你保证,在本章结束之前,你肯定能够开发一个简单的Spring应用。首先,我将使用Spring的一些基础概念为你搭建一个舞台,帮助你理解Spring是如何运行起来的。

举例来说,假设在应用的众多组件中,有两个是我们需要处理的:库存服务(用来获取库存水平)和商品服务(用来提供基本的商品信息)。商品服务需要依赖于库存服务,这样它才能提供商品的完整信息。图1.1阐述这些bean和Spring应用上下文之间的关系。
epub_29101559_7

图1.1 应用组件通过Spring的应用上下文来进行管理并实现互相注入

在核心容器之上,Spring及其一系列的相关库提供了Web框架、各种持久化可选方案、安全框架、与其他系统集成、运行时监控、微服务支持、反应式编程以及众多现代应用开发所需的特性。

在历史上,指导Spring应用上下文将bean装配在一起的方式是使用一个或多个XML文件(描述各个组件以及它们与其他组件的关联关系)。例如,如下的XML描述了两个bean,也就是InventoryService bean和ProductService bean,并且通过构造器参数将InventoryService装配到了ProductService中:

1
2
3
4
5
6
<bean id="inventoryService"
class="com.example.InventoryService" />
<bean id="productService"
class="com.example.ProductService" />
<constructor-arg ref="inventoryService" />
</bean>

但是,在最近的Spring版本中,基于Java的配置更为常见。如下基于Java的配置类是与XML配置等价的:

1
2
3
4
5
6
7
8
9
10
11
@Configuration
public class ServiceConfiguration {
@Bean
public InventoryService inventoryService() {
return new InventoryService();
}
@Bean
public ProductService productService() {
return new ProductService(inventoryService());
}
}

@Configuration注解会告知Spring这是一个配置类,会为Spring应用上下文提供bean。这个配置类的方法使用@Bean注解进行了标注,表明这些方法所返回的对象会以bean的形式添加到Spring的应用上下文中(默认情况下,这些bean所对应的bean ID与定义它们的方法名称是相同的)。

相对于基于XML的配置方式,基于Java的配置会带来多项额外的收益,包括更强的类型安全性以及更好的重构能力。即便如此,不管是使用Java还是使用XML的显式配置,只有当Spring不能进行自动配置的时候才是必要的。

在Spring技术中,自动配置起源于所谓的自动装配(autowiring)和组件扫描(component scanning)。借助组件扫描技术,Spring能够自动发现应用类路径下的组件,并将它们创建成Spring应用上下文中的bean。借助自动装配技术,Spring能够自动为组件注入它们所依赖的其他bean。

最近,随着Spring Boot的引入,自动配置的能力已经远远超出了组件扫描和自动装配。Spring Boot是Spring框架的扩展,提供了很多增强生产效率的方法。最为大家所熟知的增强方法就是自动配置(autoconfiguration),Spring Boot能够基于类路径中的条目、环境变量和其他因素合理猜测需要配置的组件并将它们装配在一起。

我非常愿意为你展现一些关于自动配置的示例代码,但是我做不到。自动配置就像风一样,你可以看到它的效果,但是我找不到代码指给你说,“看!这就是自动配置的样例!”事情发生了,组件启用了,功能也提供了,但是不用编写任何代码。没有代码就是自动装配的本质,也是它如此美妙的原因所在。

Spring Boot大幅度减少了构建应用所需的显式配置的数量(不管是XML配置还是Java配置)。实际上,当完成本章的样例时,我们会有一个可运行的Spring应用,该应用只有一行Spring配置代码。

Spring Boot极大地改善了Spring的开发,因此很难想象在没有它的情况下如何开发Spring应用。因此,本书会将Spring和Spring Boot当成一回事。我们会尽可能多地使用SpringBoot,只有在必要的时候才使用显式配置。因为Spring XML配置是一种过时的方式,所以我们主要关注Spring基于Java的配置。

Spring Boot极大地改善了Spring的开发,因此很难想象在没有它的情况下如何开发Spring应用。因此,本书会将Spring和Spring Boot当成一回事。我们会尽可能多地使用SpringBoot,只有在必要的时候才使用显式配置。因为Spring XML配置是一种过时的方式,所以我们主要关注Spring基于Java的配置。

第1部分 Spring基础

本书的第1部分将会介绍如何开始编写Spring应用,并在这个过程中学习Spring的基础知识。

在第1章中,我将简要介绍Spring和Spring Boot的核心知识,并展示在构建第一个Spring应用Taco Cloud的过程中如何初始化Spring项目。在第2章中,我们将深入研究Spring MVC,了解如何在浏览器中显示模型数据,以及如何处理和验证表单输入。我们还会介绍选择视图模板库的技巧。在第3章中,我们将向Taco Cloud应用程序添加数据持久化功能。到时候,我们将介绍如何使用Spring的JDBC模板来插入数据,以及如何使用Spring Data声明JPA repository。第4章将介绍Spring应用程序的安全性,包括自动配置Spring安全性、声明自定义用户存储、自定义登录页面以及防止跨站请求伪造(CSRF)攻击。作为第1部分的结尾,我们将在第5章中学习配置属性。我们将了解如何细粒度调整自动配置bean、让应用组件使用配置属性,以及如何使用Spring profile。

关于本书

编写《Spring实战(第5版)》的目的是让读者学会使用Spring框架、Spring Boot以及Spring生态系统中各种辅助部分构建令人赞叹的应用程序。本书首先介绍如何使用Spring和Spring Boot开发基于Web、以数据库作为后端的Java应用;随后进行必要的扩展,展现如何与其他应用进行集成、使用反应式类型进行编程,以及将应用拆分为离散的微服务;最后讨论如何准备应用的部署。

尽管Spring生态系统中的每个项目都提供了完善的文档,但是本书所做的是所有参考文档都无法做到的事情:提供一个实用的、项目驱动的指南,将Spring的各种元素组合起来形成一个真正的应用。

谁应该阅读这本书

《Spring实战(第5版)》适用于刚刚开始学习Spring Boot和Spring框架的Java开发人员,也适用于想要超越基础知识并学习Spring新特性的经验丰富的Spring开发者。

这本书是如何组织的:路线图

本书分成了5个部分,共计19章。
第1部分涵盖构建Spring应用的基础话题。

  • 第1章介绍Spring和Spring Boot以及如何初始化Spring项目。在本章中,我们迈出构建Spring应用的第一步,在本书后续各章中,我们会对这个应用进行扩展。
  • 第2章讨论如何使用Spring MVC构建应用的Web层。在本章中,我们将会构建处理Web请求的控制器以及在浏览器中渲染信息的视图。
  • 第3章会深入探讨Spring应用的后端,在这里数据会持久化到关系型数据库中。
  • 在第4章中,我们会使用Spring Security认证用户并防止未认证的用户访问应用。
  • 第5章介绍如何使用Spring Boot的配置属性功能来配置Spring应用。我们还会学习如何使用profile选择性地应用配置。

第2部分讨论如何将Spring应用与其他应用进行集成。

  • 第6章延续第2章对Spring MVC的讨论,我们将会学习如何在Spring中编写REST API。
  • 第7章讨论了和第6章相对立的主题,展现Spring应用如何消费REST API。
  • 第8章会讨论如何使用异步通信技术让Spring应用发送和接收消息,这里会用到Java Message Service、RabbitMQ或Kafka。
  • 第9章讨论如何使用Spring Integration进行声明式的应用集成。

第3部分探讨Spring对反应式编程提供的全新支持。

  • 第10章介绍Reactor项目。这是一个反应式编程库,支撑了Spring 5的反应式特性。
  • 第11章重新探讨REST API开发,介绍全新的Web框架Spring WebFlux。该框架借用了很多Spring MVC的理念,但是为Web开发提供了新的反应式模型。
  • 第12章将会看一下如何使用Spring Data编写反应式数据持久化,我们将会读取和写入Cassandra与Mongo数据库。

第4部分将会拆分单体应用模型,介绍Spring Cloud和微服务开发。

  • 第13章会深入介绍服务发现,组合使用Spring和Netflix的注册中心实现Spring微服务的注册和发现。
  • 第14章将展现如何在配置服务器中实现中心化的应用配置,从而实现跨微服务共享配置。
  • 第15章会介绍Hystrix的断路器模式。它能够让微服务在面临失败时更有弹性。

在第5部分中,我们将会讨论如何做好将应用投入生产环境的准备,并看一下如何进行部署。

  • 第16章会介绍Spring Boot Actuator。它是Spring Boot的一个扩展,通过REST端点的形式暴露Spring应用内部的运行状况。
  • 第17章将会介绍如何使用Spring Boot Admin。它是构建在Actuator之上的一个用户友好的基于浏览器的管理应用。
  • 第18章将会讨论如何将Spring bean暴露为JMX MBean以及如何消费它们。
  • 在第19章中,我们会看到如何将Spring应用部署到各种生产环境中。

通常来讲,刚刚接触Spring的开发人员应该从第1章开始,并按顺序阅读每一章;经验丰富的Spring开发人员可能更愿意在任何感兴趣的时候参与进来。即便如此,每一章都是建立在前一章内容的基础上的,所以如果从中间开始阅读,那么可能会漏掉一些上下文信息。

关于代码

本书包含许多源代码的样例,有的是编号的程序清单,有的是普通文本内嵌的源码。在这两种情况下,源代码都使用固定宽度的字体排版,以便将其与普通文本分开。有时代码也会使用粗体显示,以便于强调此处代码与本章前面步骤的变更,比如为已有的代码行添加新的特性。

在许多情况下,原始源代码会重新格式化;我们添加了换行符和重新缩进,以适应书中可用的页面空间。在极少数情况下,这样做依然是不够的,在这种情况下程序清单会包括换行符(➥)。此外,当在文中描述代码的时候,源码中的注释通常会被移除。许多程序清单会有代码标注,用来突出强调重要的概念。

本书中样例的源码可以通过异步社区的本书页面下载。

其他在线资源

还需要其他帮助吗?

  • Spring的Web站点有很多有用的起步指南(其中一部分就是由本书的作者编写的)。
  • StackOverflow上的Spring标签和Spring Boot是一个询问Spring问题和帮助别人的好地方。帮助解决别人的Spring问题是学习Spring的好办法。

关于作者

克雷格·沃斯(Craig Walls)是Pivotal的首席工程师。他是Spring框架的热心推动者,经常在本地用户组和会议上发言,撰写关于Spring的文章。在不琢磨代码的时候,Craig正在计划去迪士尼世界或迪士尼乐园的下一次旅行,他希望尽可能多地陪伴他的妻子和两个女儿。

关于封面

《Spring实战(第5版)》的封面人物是“Le Caraco”,也就是约旦西南部卡拉克(Karak)省的居民。该省的首府是Al-Karak,那里的山顶有座城堡,对死海和周边的平原有着极佳的视野。这幅图出自1796年出版的法国旅游图书,Encyclopédiedes Voyages,由J.G.St.Sauveur编写。在那时,为了娱乐而去旅游还是相对新鲜的做法,而像这样的旅游指南是很流行的,它能够让旅行家和足不出户的人们了解法国其他地区和国外的居民。

Encyclopédiedes Voyages中多种多样的图画生动描绘了200年前世界上各个城镇和地区的独特魅力。在那时,相隔几十千米的两个地区着装就不相同,可以通过着装判断人们究竟属于哪个地区。这本旅行指南展现了那个时代和其他历史时代的隔离感和距离感,这与我们这个运动过度的时代是截然不同的。

从那以后,服装风格发生了改变,富有地方特色的多样性开始淡化。现在,有时很难说一个洲的居民和其他洲的居民有什么不同。可能,从积极的方面来看,我们用原来文化和视觉上的多样性换来了个人风格的多变性,或者可以说是更为多样化和有趣的知识科技生活。这本旅行指南中的图片反映了两个世纪前各个地区生活的多样性,我们现在用图书封面的方式对其进行了再现。Manning出版社的员工都认为这是计算机行业中一个很有意思的创意。