Spring Boot 与 Docker
观察 GraphQL 的实际运行

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 服务中的相同。您无需使用该指南来利用本指南,尽管比较结果可能会很有趣。

您需要什么

如何完成本指南 {#_how_to_complete_this_guide}

与大多数 Spring 入门指南一样,您可以从头开始并完成每个步骤,或者可以跳过您已经熟悉的基本设置步骤。无论哪种方式,您最终都能获得可运行的代码。

从头开始,请继续阅读使用 Spring Initializr 开始

跳过基础知识,请执行以下操作:

完成后,您可以将您的结果与 gs-actuator-service/complete 中的代码进行对比。

从 Spring Initializr 开始

您可以使用这个预初始化项目,然后点击生成以下载一个ZIP文件。该项目已配置为适合本教程中的示例。

要手动初始化项目:

  1. 访问 https://start.spring.io。该服务会拉取应用程序所需的所有依赖项,并为您完成大部分设置。

  2. 选择 Gradle 或 Maven 以及您想要使用的语言。本指南假设您选择了 Java。

  3. 点击 Dependencies 并选择 Spring WebSpring Boot Actuator

  4. 点击 Generate

  5. 下载生成的 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 添加了一些有用的内置服务。

另请参阅

以下指南可能也会有所帮助:

本页目录