第2章 微服务构建:Spring Boot
1.框架简介
通过Maven等构建工具来创建针对不同场景的脚手架工程,在需要新建项目时通过这些脚手架来初始化我们定义的标准工程,并根据需要
做一些简单的修改以达到简化原有配置过程的效果。
Spring Boot的出现可以有效改善这类问题,Spring Boot的宗旨并非要重写Spring或者替代Spring,而是希望通过设计大量的自动化
配置等方式简化Spring原有样板化的配置,使得开发者可以快速构建应用。
除了解决配置问题,Spring Boot 还通过一系列 Starter POMs 的定义,让我们整合各项功能的时候,不需要在Maven的 pom.xml
中维护那些错综复杂的依赖关系,而是通过类似模块化的Starter模块定义来引用,使得依赖管理工作变得更简单。
如今容器化大行其道的时代,Spring Boot 除了可以很好的融入Docker之外,其自身支持嵌入式的Tomcat,Jetty等容器。所以,通过
Spring Boot构建的应用不再需要安装Tomcat,将应用打包成war,再部署到Tomcat主要复杂的构建与部署动作,只需将Spring Boot打包
成jar包,并通过 java -jar 命令直接运行就能启动一个标准化的web应用,这使得Spring Boot应用变得非常轻便。
Spring Boot 对于构建、部署等做了这么多优化,自然不能少了对开发环节的优化。整个Spring Boot的生态系统都使用到了Groovy,
很自然的,我们完全可以通过Gradle和Groovy来开发Spring Boot应用。
2.快速入门
a) 构建Maven项目:
1.通过官方的Spring Initializr工具来产生基础项目
2.访问 https://start.spring.io,该页面提供了以Maven或者Gradle构建Spring Boot项目的功能
3.选择构建工具Maven Project、Spring Boot 版本选择 1.3.7,填写Group和Artifact信息,在Search for dependencies
中可以依赖的包
4.单击 Generate Project 按钮下载项目压缩包
5.解压项目包,并用IDE以Maven项目导入
6.从菜单中选择File->New->Project from Existing Sources ...
7.选择解压后的项目文件夹,单击Ok按钮
8.单击Import project from external model 并选择Maven,一直单击Next按钮
b) 工程结构解析
Spring Boot 的基础结构有三大块:
1.src/main/java
主程序入口,可以通过直接运行该类来启动Spring Boot应用。
2.src/main/resources
配置目录,该目录下存放应用的一些配置信息,比如应用名、服务端口、数据库链接等。由于我们引入了Web模块,因此产生了
static目录与templates目录,前者用于存放静态资源,后者用于存放web也页面的模板文件。
3.src/test
单元测试目录,生成的HelloApplicationTests通过JUnit4实现,可以直接用运行Spring Boot应用的测试。
3.Maven配置解析
4.0.0
org.springframework.boot
spring-boot-starter-parent
2.6.7
com.weidaodao
hello
0.0.1-SNAPSHOT
hello
Demo project for Spring Boot
1.8
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-test
test
org.springframework.boot
spring-boot-maven-plugin
在基础信息部分,groupId和artifactId 对应生成项目时页面上输入的内容。另外,打包形式为jar:jar
,正如我们所介绍的,Spring Boot默认将该web应用打包为jar的形式,而非war的形式,因为默认的web模块依赖会包含嵌入式tomcat,这样
使得我们的应用jar自身就具备了提供web服务的能力。
父项目parent配置指定为 spring-boot-starter-parent 的1.3.7版本,该父项目中定义了 Spring Boot 版本的基础依赖以及
一些默认配置内容,比如,配置文件 application.properties的位置等。
在项目依赖dependencies配置中,包含了下面2项:
a)spring-boot-starter-web:全栈web开发模块,包含嵌入式Tomcat,Spring MVC
b)spring-boot-starter-test:通用测试模块,包含JUnit、Hamcrest、Mockito
这里所引用的web和test模块,在Spring Boot生态中被称为 Starter POMs。Starter POMs是一系列轻便的依赖包,是一套一站式
的Spring相关技术的解决方案。开发者在试用和整合模块时,不必再去搜索样例代码中的依赖配置复制使用,只需要引入对应的模块包即可。
比如,开发web应用时,就引入spring-boot-starter-web,希望应用具备范围内数据库能力的时候,就引入spring-boot-starter-jdbc,
或者是更好用的spring-boot-starter-jpa。在使用Spring Boot构建应用的时候,各项功能模块的整合不再像传统Spring应用的开发方式
那样,需要在pom.xml中做大量的依赖配置,而是通过使用Starter POMs定义的依赖包,使得功能模块整合变得非常轻巧,易于理解和使用。
Spring Boot的Starter POMs采用 spring-boot-starter-*的命名方式,*代表一个特别的应用功能模块,比如这里的web、test。
Spring Boot工程本身十分简单,大量的学习要点还是将来在对这些Starter POMs的使用之上。
4.实现RESTful API
在Spring Boot中创建一个RESTful API的实现代码如同Spring MVC应用一样,只是不需要像Spring MVC那样先做很多配置。如下:
1.新建package,命名为 com.didispace.web
2.新建HelloControoler类,内容如下:
package com.weidaodao.web;
import org.springframework.web.bind.annotation.RequestMapping;
public class HelloController {
@RequestMapping("/hello")
public String index() {
return "hello,dao";
}
}
3.启动该应用,通过浏览器访问 http://localhost:8080/hello
a)启动Spring Boot应用
1.作为一个Java应用程序,可以直接通过运行拥有main函数的类来启动;
2.在Maven配置中,之前提到了spring-boot插件,可以使用它来启动,比如执行 mvn spring-boot:run 命令,或者是单机IDE
中对Maven插件的工具;
3.在服务器上部署运行时,通常先使用mvn install 将应用打包成jar包,再通过java -jar xxx.jar 来启动
b) 编写测试用例
package com.weidaodao;
import org.junit.jupiter.api.Test;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = HelloApplication.class)
@WebAppConfiguration
public class HelloApplicationTests {
private MockMvc mvc;
@Before
public void setUp() throws Exception {
mvc = MockMvcBuilders.standaloneSetup(new HelloController()).build();
}
@Test
public void hello() throws Exception() {
mvc.perform(MockMvcRequestBuilders.get("/hello").accept(MediaType.APPLICATION_JSON)).andExpect(status().isOk()).andExpect(content().string(equals("Hello World")));
}
}
代码解析如下:
1.@RunWith(SpringJUnit4ClassRunner.class):引入Spring对JUnit4的支持
2.@SpringApplicationConfiguration(classes = HelloApplication.class):指定Spring Boot的启动类
3.@WebAppConfiguration:开启web应用的配置,用于模拟ServletContext
4.MockMvc对象:用于模拟调用Controller的接口发起请求,在@Test定义的hello测试用例中,perform函数执行一次请求调用,
accept用于执行接收的数据类型,andExcept用于判断接口返回的期望值
5.@Before:JUnit中定义在测试用例@Test内容执行前预加载的内容,这里用来初始化对HelloController的模拟
5.配置详解
1.配置文件
在快速入门时我们介绍过 Spring Boot 的工程结构时,提到过 src/main/resources 目录是Spring Boot的配置目录,所以当
要为应用创建个性化配置时,应该在该目录下进行。
Spring Boot的默认配置文件位置为 src/main/resources/application.properties。关于Spring Boot应用的配置内容都
可以集中在该文件中,根据我们引入的不同Starter模块,可以在这里定义容器端口号、数据库连接信息、日志级别等各种配置信息。
Spring Boot的配置文件除了可以使用传统的properties文件之外,还支持yaml文件。
2.自定义参数
除了可以在 Spring Boot 的配置文件中设置各个 Starter 模块中预定义的配置属性,也可以在配置中定义一些我们需要的自定义属性。
比如在 application.properties中添加:
book.name=SpringCloudInAction
然后,在应用中可以通过@Value注解来加载这些自定义的参数,比如:
@Value("${book.name}")
@Value注解加载属性值的时候可以支持两种表达式进行配置,如下:
1.一种是上面的PlaceHolder方式,格式为${...},大括号内为PlaceHolder;
2.另外一种是使用SpEL表达式,格式为#{...},大括号内为EpEL表达式。
3.参数引用
在 application.properties 中的各个参数之间可以直接通过使用PlaceHolder的方式来进行引用,比如:
book.name=SpringCloud
book.desc=${book.name} is writing <<${book.name}>>
4.使用随机数
有些特殊情况下,我们希望有些参数每次被加载的时候不是一个固定的值。可以通过使用 ${random}配置来产生。
5.命令行参数
用命令行方式启动Spring Boot应用时,连续的2个减号--就是对application.properties中的属性值进行赋值的标识。所以,
java -jar xxx.jar --server.port=8888 命令,等于在application.properties中添加属性server.port=8888。
6.多环境配置
在Spring Boot中,多环境配置的文件名需要满足 application-{profile}.properties 的格式,其中{profile}对应你的
环境标识,如下所示:
a) application-dev.properties:开发环境
b) application-test.properties:测试环境
c) application-prod.properties:生产环境
至于哪个配置文件会被加载,需要在application.properties文件中通过spring.profiles.active 属性来设置,其值对应配置
文件中的{profile}值。如 spring.profiles.active=test就会加载 application-test.properties配置文件内容。
7.加载顺序
为了能够更合理的重写各属性的值,Spring Boot使用了下面这种较为特别的属性加载顺序:
1.在命令行中传入参数
2.SPRING_APPLICATION_JSON中的属性。SPRING_APPLICATION_JSON是以JSON格式配置在系统环境变量中的内容
3.java:comp/env 中的JNDI属性
4.Java的系统属性,可以通过System.getProperties()获得的内容
5.操作系统的环境变量
6.通过random.*配置的随机属性
7.位于当前应用jar包之外,针对不同{profile}环境的配置文件内容,例如application-{profile}.properties或是yaml定义
的配置文件
8.位于当前应用jar包之内,针对不同{profile}环境的配置文件内容,例如application-{profile}.properties或是yaml定义
的配置文件
9.位于当前应用jar包之外的application.properties和yaml配置内容
10.位于当前应用jar包之内的application.properties和yaml配置内容
11.在@Configuration注解修改的类中,通过@PropertySource注解定义的属性
12.应用默认属性,使用SpringApplication.setDefaultProperties定义的内容
优先级按上面的顺序由高到低,数字越小优先级越高。
可以看到7和9,都是从应用jar包之外读取配置文件,所以,实现外部化配置的原理就是从此切入,为其指定外部配置文件的加载位置来
取代jar包之内的配置内容。
6.监控与管理
我们需要实现一套自动化的监控运维机制,而这套机制的运行基础就是不间断的收集各个微服务应用的各项指标情况。
我们开发了一套专门用于植入各个微服务应用的接口供监控系统采集信息。而这些接口往往有很大一部分指标都是类似的,比如环境变量、
垃圾收集信息、内存信息、线程池信息等。
当我们决定用 Spring Boot 来作为微服务框架时,除了它强大的快速开发功能之外,还因为它在Starter POMs 中提供了一个特殊依赖模块
spring-boot-starter-actuator。引入该模块能够自动为Spring Boot构建的应用提供一系列用于监控的端点。
1.初识actuator
在现有的Spring Boot应用中引入该模块:
1.在pom.xml的中添加 dependency 节点
org.springframework.boot
spring-boot-starter-actuator
2.7.0
2.重新启动应用
2.原生端点
spring-boot-starter-actuator 模块中已经实现的一些原生端点,可以将原生端点分为以下三大类:
1.应用配置类
获取应用程序中加载的应用配置、环境变量、自动化配置报告等与 Spring Boot 应用密切相关的配置类信息。
由于 Spring Boot 为了改善传统 Spring 应用繁杂的配置内容,采用了包扫描和自动化配置的机制来加载原本集中于xml
文件中的各项内容。虽然这样的做法让我们的代码变得非常简洁,但是整个应用的实例创建和依赖关系等信息都被离散到了各个配置
类的注解上,这使得我们分析整个应用中资源和实例的各种关系变得非常困难。而这类端点可以帮助我们轻松获取一系列关于 Spring
应用配置内容的详细报告,比如自动化配置的报告、Bean创建的报告、环境属性的报告等。
a) /autoconfig
该端点用来获取应用的自动化配置报告,其中包括所有自动化配置的候选项。同时还列出了每个候选项是否满足自动化配置的
各个先决条件。所以,该端点可以帮助我们方便的找到一些自动化配置为什么没有生效的具体原因。该报告内容将自动化配置内容
分为以下两部分内容:
1.positiveMathes 中返回的是条件匹配成功的自动化配置
2.negativeMatches 中返回的是条件匹配不成功的自动化配置
每个自动化配置候选项中都有一系列的条件。
b) /beans
该端点用来获取应用上下文中创建的所有Bean。
c) /configprops
该端点用来获取应用中配置的属性信息报告。
d) /env
该端点与/configprops 不同,它用来获取应用所有的环境属性报告。包括环境变量、jvm属性、应用的配置属性、
命令行中的参数。
e) /mappings
该端点用来返回所有 Spring MVC 的控制权映射关系报告。
f) /info
该端点用来返回一些应用自定义的信息。
2.度量指标类
获取应用程序运行过程中用于监控的度量指标,比如内存信息、线程池信息、http请求统计等。
上面介绍的应用配置类端点所提供的信息报告在应用启动的时候就已经基本确定了其返回的内容,可以说是一个静态的报告。
而度量类端点提供的报告内容则是动态变化的,这些端点提供了应用程序在运行过程中的一些快照信息,比如内存使用情况、http
请求统计、外部资源指标等。
a) /metrics
该端点用来返回当前应用的各类重要度量指标,比如内存信息、线程信息、垃圾回收信息等。
b) /helth
用来获取应用的各类健康指标信息。
c) /dump
该端点用来暴露程序运行中的线程信息。
d) /trace
该端点用来返回基本的http跟踪信息。
3.操作控制类
提供了对应用的关闭等操作功能。
前面介绍的所有端点都是用来反映应用自身的属性或是运行中的状态,相对于操作控制类端点没有那么敏感,所以它们都是默认
启用的。而操作控制类端点拥有更强大的控制能力,如果要使用它们的话,需要通过睡醒配置开启操作。
在原生端点中,只提供了一个用来关闭应用的端点:/shutdown (后续我们引入Eureka之后,会引入更多控制端点)。可以通过
如下配置开启它:
endpoints.shutdown.enabled=true