唯一不变的就是变化 - 希腊哲学家拉克利特(heraclitus)
Spring框架在不断的变化,以解决现代应用开发中的问题,微服务,反应式编程,Spring Boot等等。
1.1什么是Spring
任何实际的应用程序都是由很多组件组成的,每个组件负责整个应用功能的一部分,这些组件需要与其他的应用元素进行协调以完成自己的任务。当程序运行时,需要以某种方式创建并引入这些组件。
Spring的核心是提供了一个容器(Container),通常称为Spring应用上下文(Spring application context),会创建和管理应用组件--bean, 会在Spring应用上下文中装配在一起,从而形成一个完整的应用程序。
将bean装配在一起的行为是通过一种基于依赖注入(Dependency Injection, DI)的模式实现的。此时,组件不会再去创建他所依赖的组件并管理他们的生命周期,使用依赖注入的应用依赖于单独的实体(容器)来创建 和维护所有的组件,并将其注入到需要他们的bean中。通常,这是通过构造器参数和属性访问方法来实现的。
以前Spring通过一个或多个XML文件来进行依赖的定义,现在一般通过基于注解的方式。
1.2 初始化Spring应用
这里我认为无论通过那种方式,只要达到目的即可,没必要学习太多创建项目的方式。
所以,不同于书中介绍,我们使用Intellij Idea来创建
1.2.1 初始化项目

创建好之后,等待依赖下载完毕
1.2.2 Spring项目目录结构

- maven相关
- graddle和maven使用的都比较多,这里为了和书里面保持一致,用maven
- mvnw和mvnw.cmd: 是Maven包装器脚本(wrapper)
- pom.xml:maven构建规范
- Spring SpringBoot
- TacoCloudApplication.java SpringBoot的主类
- resources
- static: 存放静态内容
- templates: 渲染内容到浏览器的模板文件,我们选用的Thymeleaf就是一个模板的库
- application.properties 配置属性的地方
- 测试
- TacoCloudApplicationTests.java 简单的测试类
探索构建规范
<packaging>, 这里将应用构建成了一个可执行的jar文件,而不是war文件,因为所有的Java云平台都能够运行可执行的JAR文件,所以Spring Initializr基于云思维,默认都是使用JAR打包方式,当然你可以修改。
<parent>,<version>表名项目以 spring-boot-starter-parent作为其父POM,这个父POM为Spring项目常用的一些库提供了依赖管理。
<dependencies>元素下面声明了四个依赖,这些大都是我们之前创建项目的时候选择的
也可以看到好几个是starter依赖(Spring Boot Starter依赖本身不包含库代码,传递性的拉取其他库),其好处:
- 构建文件会显著减小并且易于管理,这样不必为每个所需的库都声明依赖
- 能够根据所提供的功能来思考依赖,而不是根据库的名称。
- 不必担心版本的问题
最后 pom.xml里面还有一个Spring Boot插件:
- 提供了一个maven goal,允许我们使用Maven来运行应用
- 确保依赖的所有库都会包含在可执行文件中,并且能够保证它们在运行时类路径下是可用的
- 在jar中生成一个manifest文件,将引导类声明为可执行的JAR主类
引导应用
我们通过可执行的JAR文件的形式来运行应用,所以需要有一个主类,它将会在JAR运行的时候被执行。同时还需要一个最小化的Spring配置,以引导该应用。这就是TacoCloudApplication所做的事儿。
TacoCloudApplication的 @SpringBootApplication是一个组合注解:
@SpringBootConfiguration- 将类声明为配置类,其实就是
@Configuration的特殊形式
- 将类声明为配置类,其实就是
@EnableAutoConfiguration:启用Spring Boot的自动配置@ComponentScan:启用组件扫描
TacoCloudApplication的main方法,是JAR文件执行的时候要运行的方法,这里会调用 SpringApplication 的静态run方法,后者会真正执行引用的引导过程,创建Spring应用上下文,传递的参数:
- 一个是配置类(传递给run的配置类不一定要和引导类相同,但这是最便利,最典型的做法)
- 一个是命令行参数
测试应用
这个测试类会执行必要的检查,确保Spring应用上下文能够成功加载
1.3 编写Spring应用
目标:
- 添加一个控制器类,处理主页相关的请求
- 添加一个视图模板,用来定义主页看起来是什么样子
1.3.1 处理web请求
Spring自带了一个强大的web框架,SpringMVC。
SpringMVC的核心是控制器概念,控制器是处理请求并以某种方式进行信息相应的类。在面向浏览器的应用中,控制器会填充可选的数据模型并将请求传递给一个视图,以便于生成返回给浏览器中的HTML。
下面是一个简单的控制器,用来处理对根路径("/")的请求,并将这些请求转发到主页视图。
package com.chengxuyuancd.tacocloud.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class HomeController {
@GetMapping("/")
public String home() {
return "home";
}
}
@Controller其实并没有做太多的事儿,主要目的是为了让@ComponentScan将这个类识别为一个组件。所以这里Spring会自动发现它,并创建一个HomeController的实例作为Spring应用上下文中的bean。- 而实际上,
@Service,@Component,@Repository都可以做到和@Controller一样的效果 - home()是一个简单的控制器方法,带有
@GetMapping注解,如果针对"/" 发送HTTP GET请求,则这个方法将会处理请求,返回String类型的home值。 - home()方法返回的值将会被解析成视图的逻辑名,视图可以有多种实现方式,这里将会使用Thymeleaf模板
- 模板的名称由逻辑视图名派生而来,加上"/templates/"前缀和".html"后缀,最终形成的模板路径将是:"/templates/home.html"
1.3.2 定义视图
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Taco CLoud</title>
</head>
<body>
<h1>Welcome to...</h1>
<img th:src="@{/images/TacoCloud.png}">
</body>
</html>
这里使用了Thymeleaf的th:src属性和 @{...}表达式,用来引用相对于上下文路径的图片作为img的参数。
Spring中静态资源的地址默认是: /src/main/resources/static
1.3.3 测试控制器
一般的测试,对HTML页面进行断言比较困难,但Spring测试提供了一些支持。
我们期望测试对根路径("/")发送一个HTTP GET请求并期望得到成功的结果,结果应该包含"Welcome to..."
package com.chengxuyuancd.tacocloud;
import com.chengxuyuancd.tacocloud.controller.HomeController;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.test.web.servlet.MockMvc;
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.*;
@WebMvcTest(HomeController.class)
class TacoCloudApplicationTests {
@Autowired
private MockMvc mockMvc;
@Test
void contextLoads() {
}
@Test
void testHomePage() throws Exception {
mockMvc.perform(get("/"))
.andExpect(status().isOk())
.andExpect(view().name("home"))
.andExpect(content().string(containsString("Welcome to...")));
}
}
这里我们没有使用 @SpringBootTest 注解,而是使用 @WebMvcTest,他会让测试在Spring MVC应用的上下文中执行,具体的是,这里会将HomeController注册到Spring MVC中,这样就可以向他发送请求了。
@WebMvcTest也会为测试Spring MVC应用提供Spring环境支持,仿造了Spring MVC的运行机制,测试类被注入了一个MockMvc,能够让测试实现mockup。
测试testHomePage()方法中,首先使用MockMvc对象对"/"根路径发送HTTP GET请求,设置的预期如下:
- 相应应该返回HTTP 200(OK)状态码
- 视图的逻辑名称应该是home
- 渲染后的视图应该包含文本"Welcome to..."
1.3.4 构建和运行测试
这里我提供两种我常用的方式:
- 直接运行 TacoCloudApplication.java文件
- 使用maven运行:
./mvnw spring-boot:run
控制台会打印出来Spring的ASCII字符,以及Tomcat已经在8080端口启动的日志

1.3.5 Spring Boot DevTools
DevTools为开发人员提供了很多便利:
- 代码变更后应用会自动重启
- 面相浏览器的资源(如模板,JavaScript,样式表)等发生变化的时候,自动刷新浏览器
- 自动禁用模板缓存
- 如果使用H2数据库,内置了H2的控制台
1.3.6 回顾一下
我们执行了如下步骤:
- 使用Spring Initializr创建初始的项目结构
- 编写控制器类处理针对主页的请求
- 定义一个视图模板来渲染主页
- 编写一个简单的测试类来验证工作符合预期
1.4 Spring 概览
1.4.1 Spring核心框架
Spring的核心中有一个是Spring MVC
数据持久化的基础操作
最新版的Spring中,添加了反应式(reactive)风格编程的支持,包括Spring WebFlux
1.4.2 Spring Boot
1.4.3 Spring Data
关系型数据库(JPA),文档数据库(Mongo),图数据库(Neo4j)