Spring Boot Actuator 是 Spring Boot 的一个子项目。它几乎不需要您额外的工作,就能为您的应用程序添加多个生产级别的服务。在本指南中,您将构建一个应用程序,然后了解如何添加这些服务。
你将构建的内容
本指南将带您创建一个使用 Spring Boot Actuator 的“Hello, world” RESTful Web 服务。您将构建一个接受以下 HTTP GET 请求的服务:
$ curl http://localhost:9000/hello-world
它返回以下 JSON:
{"id":1,"content":"Hello, World!"}
此外,还添加了许多功能,用于在生产(或其他)环境中管理服务。您构建的服务的业务功能与构建 RESTful Web 服务中的相同。您无需使用该指南来利用本指南,尽管比较结果可能会很有趣。
您需要什么
-
大约 15 分钟
-
一个常用的文本编辑器或IDE
-
Java 17 或更高版本
-
您也可以直接将代码导入到您的 IDE 中:
如何完成本指南 {#_how_to_complete_this_guide}
与大多数 Spring 入门指南一样,您可以从头开始并完成每个步骤,或者可以跳过您已经熟悉的基本设置步骤。无论哪种方式,您最终都能获得可运行的代码。
要从头开始,请继续阅读使用 Spring Initializr 开始。
要跳过基础知识,请执行以下操作:
-
下载并解压本指南的源代码仓库,或者使用 Git 克隆它:
git clone https://github.com/spring-guides/gs-actuator-service.git
-
进入
gs-actuator-service/initial
目录 -
跳转到 创建一个表示类。
完成后,您可以将您的结果与 gs-actuator-service/complete
中的代码进行对比。
从 Spring Initializr 开始
您可以使用这个预初始化项目,然后点击生成以下载一个ZIP文件。该项目已配置为适合本教程中的示例。
要手动初始化项目:
-
访问 https://start.spring.io。该服务会拉取应用程序所需的所有依赖项,并为您完成大部分设置。
-
选择 Gradle 或 Maven 以及您想要使用的语言。本指南假设您选择了 Java。
-
点击 Dependencies 并选择 Spring Web 和 Spring Boot Actuator。
-
点击 Generate。
-
下载生成的 ZIP 文件,这是一个根据您的选择配置的 Web 应用程序的归档文件。
如果您的 IDE 集成了 Spring Initializr,您可以直接在 IDE 中完成这个过程。
您还可以从 Github 上 fork 该项目,并在您的 IDE 或其他编辑器中打开它。
运行空服务
Spring Initializr 创建了一个空应用程序,您可以用它来开始开发。以下示例(来自 initial
目录中的 src/main/java/com/example/actuatorservice/ActuatorServiceApplication
)展示了由 Spring Initializr 创建的类:
package com.example.actuatorservice;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class ActuatorServiceApplication {
public static void main(String[] args) {
SpringApplication.run(ActuatorServiceApplication.class, args);
}
}
@SpringBootApplication
注解根据类路径内容和其他因素提供了大量默认配置(例如嵌入式 servlet 容器)。它还启用了 Spring MVC 的 @EnableWebMvc
注解,从而激活了 Web 端点。
虽然此应用程序中没有定义任何端点,但已经足够启动并查看 Actuator 的部分功能。SpringApplication.run()
命令知道如何启动 Web 应用程序。您只需运行以下命令:
$ ./gradlew clean build && java -jar build/libs/gs-actuator-service-0.1.0.jar
您尚未编写任何代码,那么发生了什么情况呢?要查看答案,请等待服务器启动,打开另一个终端,并尝试以下命令(显示其输出):
$ curl localhost:8080
{"timestamp":1384788106983,"error":"Not Found","status":404,"message":""}
前面的命令输出表明服务器正在运行,但您尚未定义任何业务端点。与容器生成的默认 HTML 错误响应不同,您看到的是来自 Actuator /error
端点的通用 JSON 响应。您可以在服务器启动的控制台日志中查看默认提供了哪些端点。您可以尝试其中一些端点,包括 /health
端点。以下示例展示了如何操作:
$ curl localhost:8080/actuator/health
{"status":"UP"}
状态为 UP
,表示 actuator 服务正在运行。
有关更多详细信息,请参阅 Spring Boot 的 Actuator 项目。
创建表示类
首先,您需要仔细考虑您的 API 将会是什么样子。
您希望处理对 /hello-world
的 GET 请求,可以选择性地包含一个 name 查询参数。作为对此类请求的响应,您希望返回表示问候的 JSON,其内容大致如下:
{
"id": 1,
"content": "Hello, World!"
}
id
字段是问候语的唯一标识符,content
包含了问候语的文本表示。
为了对问候语表示进行建模,创建一个表示类。以下清单(来自 src/main/java/com/example/actuatorservice/Greeting.java
)展示了 Greeting
类:
package com.example.actuatorservice;
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;
}
}
现在您需要创建将提供表示类的端点控制器。
创建资源控制器
在 Spring 中,REST 端点是 Spring MVC 控制器。以下 Spring MVC 控制器(来自 src/main/java/com/example/actuatorservice/HelloWorldController.java
)处理对 /hello-world
端点的 GET 请求,并返回 Greeting
资源:
package com.example.actuatorservice;
import java.util.concurrent.atomic.AtomicLong;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class HelloWorldController {
private static final String template = "Hello, %s!";
private final AtomicLong counter = new AtomicLong();
@GetMapping("/hello-world")
@ResponseBody
public Greeting sayHello(@RequestParam(name="name", required=false, defaultValue="Stranger") String name) {
return new Greeting(counter.incrementAndGet(), String.format(template, name));
}
}
面向人类的控制器和REST端点控制器之间的关键区别在于响应的创建方式。端点控制器不依赖于视图(如JSP)将模型数据渲染成HTML,而是返回数据直接写入响应体。
@ResponseBody注解告诉Spring MVC不要将模型渲染成视图,而是将返回的对象写入响应体。它通过使用Spring的消息转换器来实现这一点。由于Jackson 2在类路径中,如果请求的Accept
头指定返回JSON,MappingJackson2HttpMessageConverter将处理Greeting
对象到JSON的转换。
如何确认 Jackson 2 是否在 classpath 中?您可以运行
mvn dependency:tree
或./gradlew dependencies
,这样会得到包含 Jackson 2.x 的详细依赖树。您还可以看到它来自 /spring-boot-starter-json,而该依赖本身是由 spring-boot-starter-web 引入的。
运行应用程序
您可以从自定义的主类或直接从某个配置类中运行该应用程序。对于这个简单的示例,您可以使用 SpringApplication
辅助类。请注意,这是 Spring Initializr 为您创建的应用程序类,对于这个简单的应用程序,您甚至无需修改它即可使其正常工作。以下代码(来自 src/main/java/com/example/actuatorservice/HelloWorldApplication.java
)展示了该应用程序类:
package com.example.actuatorservice;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class HelloWorldApplication {
public static void main(String[] args) {
SpringApplication.run(HelloWorldApplication.class, args);
}
}
在传统的 Spring MVC 应用程序中,您会添加 @EnableWebMvc
来开启关键行为,包括配置 DispatcherServlet
。但 Spring Boot 在检测到类路径上有 spring-webmvc 时,会自动启用此注解。这为您在后续步骤中构建控制器做好了准备。
@SpringBootApplication
注解还引入了 @ComponentScan 注解,它告诉 Spring 扫描 com.example.actuatorservice
包以查找这些控制器(以及其他带有注解的组件类)。
构建可执行 JAR 文件
您可以使用 Gradle 或 Maven 从命令行运行应用程序。您还可以构建一个包含所有必要依赖项、类和资源的单一可执行 JAR 文件并运行它。构建可执行 JAR 文件可以方便地在整个开发生命周期中、跨不同环境等将服务作为应用程序进行交付、版本控制和部署。
如果您使用 Gradle,可以通过 ./gradlew bootRun
来运行应用程序。或者,您可以使用 ./gradlew build
构建 JAR 文件,然后按如下方式运行 JAR 文件:
java -jar build/libs/gs-actuator-service-0.1.0.jar
如果您使用 Maven,可以通过 ./mvnw spring-boot:run
来运行应用程序。或者,您可以使用 ./mvnw clean package
构建 JAR 文件,然后运行该 JAR 文件,如下所示:
java -jar target/gs-actuator-service-0.1.0.jar
这里描述的步骤创建一个可运行的JAR文件。您也可以构建一个经典的WAR文件。
一旦服务运行起来(因为您在终端中运行了 spring-boot:run
),您可以在另一个终端中运行以下命令来测试它:
$ curl localhost:8080/hello-world
{"id":1,"content":"Hello, Stranger!"}
切换到不同的服务器端口 {#_switch_to_a_different_server_port}
Spring Boot Actuator 默认在 8080 端口运行。通过添加一个 application.properties
文件,您可以覆盖该设置。以下清单(来自 src/main/resources/application.properties
)展示了进行必要更改后的文件内容:
server.port: 9000
management.server.port: 9001
management.server.address: 127.0.0.1
在终端中运行以下命令以再次启动服务器:
$ ./gradlew clean build && java -jar build/libs/gs-actuator-service-0.1.0.jar
服务现在在端口 9000 启动。
您可以通过在终端中运行以下命令来测试它是否在端口 9000 上正常工作:
$ curl localhost:8080/hello-world
curl: (52) Empty reply from server
$ curl localhost:9000/hello-world
{"id":1,"content":"Hello, Stranger!"}
$ curl localhost:9001/actuator/health
{"status":"UP"}
测试您的应用程序
为了检查您的应用程序是否正常工作,您应该为应用程序编写单元测试和集成测试。src/test/java/com/example/actuatorservice/HelloWorldApplicationTests.java
中的测试类确保了
- 您的控制器响应正常。
- 您的管理端点响应正常。
需要注意的是,测试会在一个随机端口上启动应用程序。以下清单展示了测试类:
/*
* Copyright 2012-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.actuatorservice;
import java.util.Map;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.boot.test.web.server.LocalServerPort;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.test.context.TestPropertySource;
import static org.assertj.core.api.BDDAssertions.then;
/**
* Basic integration tests for service demo application.
*
* @author Dave Syer
*/
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@TestPropertySource(properties = {"management.port=0"})
public class HelloWorldApplicationTests {
@LocalServerPort
private int port;
@Value("${local.management.port}")
private int mgt;
@Autowired
private TestRestTemplate testRestTemplate;
@Test
public void shouldReturn200WhenSendingRequestToController() throws Exception {
@SuppressWarnings("rawtypes")
ResponseEntity<Map> entity = this.testRestTemplate.getForEntity(
"http://localhost:" + this.port + "/hello-world", Map.class);
then(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
}
@Test
public void shouldReturn200WhenSendingRequestToManagementEndpoint() throws Exception {
@SuppressWarnings("rawtypes")
ResponseEntity<Map> entity = this.testRestTemplate.getForEntity(
"http://localhost:" + this.mgt + "/actuator", Map.class);
then(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
}
}
总结
恭喜!您刚刚使用 Spring 开发了一个简单的 RESTful 服务,并且通过 Spring Boot Actuator 添加了一些有用的内置服务。
另请参阅
以下指南可能也会有所帮助: