Building a RESTful Web Service with Spring Boot Actuator
https://spring.io/guides/gs/actuator-service/

Building a RESTful Web Service with Spring Boot Actuator

Spring Boot Actuator is a sub-project of Spring Boot. It adds several production grade services to your application with little effort on your part. In this guide, you will build an application and then see how to add these services.

What You Will build

This guide takes you through creating a “Hello, world” RESTful web service with Spring Boot Actuator. You will build a service that accepts the following HTTP GET request:

$ curl https://localhost:9000/hello-world
COPY

It responds with the following JSON:

{"id":1,"content":"Hello, World!"}
COPY

There are also many features added to your application for managing the service in a production (or other) environment. The business functionality of the service you build is the same as in Building a RESTful Web Service. You need need not use that guide to take advantage of this one, although it might be interesting to compare the results.

What You need

How to complete this guide

Like most Spring Getting Started guides, you can start from scratch and complete each step or you can bypass basic setup steps that are already familiar to you. Either way, you end up with working code.

To start from scratch, move on to Starting with Spring Initializr.

When you finish, you can check your results against the code in gs-actuator-service/complete.

Starting with Spring Initializr

For all Spring applications, you should start with the Spring Initializr. The Initializr offers a fast way to pull in all the dependencies you need for an application and does a lot of the set up for you. This example needs the Spring Web and Spring Boot Actuator dependencies. The following image shows the Initializr set up for this sample project:


The preceding image shows the Initializr with Maven chosen as the build tool. You can also use Gradle. It also shows values of com.example and actuator-service as the Group and Artifact, respectively. You will use those values throughout the rest of this sample.

The following listing shows the pom.xml file created when you choose Maven:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="https://maven.apache.org/POM/4.0.0" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="https://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.2.2.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.example</groupId>
	<artifactId>actuator-service</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>actuator-service</name>
	<description>Demo project for Spring Boot</description>

	<properties>
		<java.version>1.8</java.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-actuator</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
			<exclusions>
				<exclusion>
					<groupId>org.junit.vintage</groupId>
					<artifactId>junit-vintage-engine</artifactId>
				</exclusion>
			</exclusions>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>

The following listing shows the build.gradle file created when you choose Gradle:

plugins {
	id 'org.springframework.boot' version '2.2.2.RELEASE'
	id 'io.spring.dependency-management' version '1.0.8.RELEASE'
	id 'java'
}

group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'

repositories {
	mavenCentral()
}

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-actuator'
	implementation 'org.springframework.boot:spring-boot-starter-web'
	testImplementation('org.springframework.boot:spring-boot-starter-test') {
		exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
	}
}

test {
	useJUnitPlatform()
}

Run the Empty Service

The Spring Initializr creates an empty application that you can use to get started. The following example (from src/main/java/com/example/actuatorservice/ActuatorServiceApplication in the initial directory) shows the class created by the 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);
  }

}
COPY

The @SpringBootApplication annotation provides a load of defaults (like the embedded servlet container), depending on the contents of your classpath and other things. It also turns on Spring MVC’s @EnableWebMvc annotation, which activates web endpoints.

There are no endpoints defined in this application, but there is enough to launch things and see some of Actuator’s features. The SpringApplication.run() command knows how to launch the web application. All you need to do is run the following command:

$ ./gradlew clean build && java -jar build/libs/gs-actuator-service-0.1.0.jar
COPY

You have yet to write any code, so what is happening? To see the answer, wait for the server to start, open another terminal, and try the following command (shown with its output):

$ curl localhost:8080
{"timestamp":1384788106983,"error":"Not Found","status":404,"message":""}
COPY

The output of the preceding command indicates that the server is running but that you have not defined any business endpoints yet. Instead of a default container-generated HTML error response, you see a generic JSON response from the Actuator /error endpoint. You can see in the console logs from the server startup which endpoints are provided out of the box. You can try a few of those endpoints, including the /health endpoint. The following example shows how to do so:

$ curl localhost:8080/actuator/health
{"status":"UP"}
COPY

Create a Representation Class

First, you need to give some thought to what your API will look like.

You want to handle GET requests for /hello-world, optionally with a name query parameter. In response to such a request, you want to send back JSON, representing a greeting, that looks something like the following:

{
    "id": 1,
    "content": "Hello, World!"
}
COPY

The id field is a unique identifier for the greeting, and content contains the textual representation of the greeting.To model the greeting representation, create a representation class. The following listing (from src/main/java/com/example/actuatorservice/Greeting.java) shows the Greeting class:

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;
  }

}
COPY

Now that you need to create the endpoint controller that will serve the representation class.

Create a Resource Controller

In Spring, REST endpoints are Spring MVC controllers. The following Spring MVC controller (from src/main/java/com/example/actuatorservice/HelloWorldController.java) handles a GET request for the /hello-world endpoint and returns the Greeting resource:

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));
  }

}
COPY

The key difference between a human-facing controller and a REST endpoint controller is in how the response is created. Rather than rely on a view (such as JSP) to render model data in HTML, an endpoint controller returns the data to be written directly to the body of the response.

The @ResponseBody annotation tells Spring MVC not to render a model into a view but, rather, to write the returned object into the response body. It does so by using one of Spring’s message converters. Because Jackson 2 is in the classpath, MappingJackson2HttpMessageConverter will handle the conversion of a Greeting object to JSON if the request’s Accept header specifies that JSON should be returned.

How do you know Jackson 2 is on the classpath? Either run ` mvn dependency:tree` or ./gradlew dependencies, and you get a detailed tree of dependencies that includes Jackson 2.x. You can also see that it comes from /spring-boot-starter-json, itself imported by spring-boot-starter-web.

Run the Application

You can run the application from a custom main class or directly from one of the configuration classes. For this simple example, you can use the SpringApplication helper class. Note that this is the application class that the Spring Initializr created for you, and you need not even modify it for it to work for this simple application. The following listing (from src/main/java/com/example/actuatorservice/HelloWorldApplication.java) shows the application class:

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);
  }

}
COPY

In a conventional Spring MVC application, you would add @EnableWebMvc to turn on key behaviors, including configuration of a DispatcherServlet. But Spring Boot turns on this annotation automatically when it detects spring-webmvc on your classpath. This sets you up to build a controller in an upcoming step.

The @SpringBootApplication annotation also brings in a @ComponentScan annotation, which tells Spring to scan the com.example.actuatorservice package for those controllers (along with any other annotated component classes).

Build an executable JAR

You can run the application from the command line with Gradle or Maven. You can also build a single executable JAR file that contains all the necessary dependencies, classes, and resources and run that. Building an executable jar so makes it easy to ship, version, and deploy the service as an application throughout the development lifecycle, across different environments, and so forth.

If you use Gradle, you can run the application by using ./gradlew bootRun. Alternatively, you can build the JAR file by using ./gradlew build and then run the JAR file, as follows:

java -jar build/libs/gs-actuator-service-0.1.0.jar

If you use Maven, you can run the application by using ./mvnw spring-boot:run. Alternatively, you can build the JAR file with ./mvnw clean package and then run the JAR file, as follows:

java -jar target/gs-actuator-service-0.1.0.jar

The steps described here create a runnable JAR. You can also build a classic WAR file.Once the service is running (because you ran spring-boot:run in a terminal), you can test it by running the following command in a separate terminal:

$ curl localhost:8080/hello-world
{"id":1,"content":"Hello, Stranger!"}
COPY

Switch to a Different Server Port

Spring Boot Actuator defaults to running on port 8080. By adding an application.properties file, you can override that setting. The following listing (from src/main/resources/application.properties)shows that file with the necessary changes:

Unresolved directive in <stdin> - include::complete/src/main/resources/application.properties[]
COPY

Run the server again by running the following command in a terminal:

$ ./gradlew clean build && java -jar build/libs/gs-actuator-service-0.1.0.jar

The service now starts on port 9000.

You can test that it is working on port 9000 by running the following commands in a terminal:

$ 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"}
COPY

Test Your Application

To check whether your application works, you should write unit and integration tests for your application. The test class in src/test/java/com/example/actuatorservice/HelloWorldApplicationTests.java ensures that

Your controller is responsive.

Your management endpoint is responsive.

Note that the tests start the application on a random port. The following listing shows the test class:

Unresolved directive in <stdin> - include::complete/src/test/java/com/example/actuatorservice/HelloWorldApplicationTests.java[]
COPY

Summary

Congratulations! You have just developed a simple RESTful service by using Spring, and you added some useful built-in services with Spring Boot Actuator.

要查看或添加评论,请登录

社区洞察

其他会员也浏览了