Spring Boot Part 1: Minimum Web Server, Devtools, and Actuator

Introduction

This series of articles will examine Spring Boot features. This first article will look at the minimum Spring Boot application, Spring Boot Devtools, and the Spring Boot Actuator. There are already a wealth of resources for Spring Boot including the Spring Initializr; these articles do not intend to replace these resources but instead provide a collection of skeletal projects which may be quickly and easily used to experiment with specific Spring Boot features.

Complete source code for the series and for this part are available on Github.

Minimum Web Server Project

The minimum web server project consists of a Maven POM, a “Launcher” class with static main function, and an applications.properties file. The web server will be launched from Maven by invoking the spring-boot:run plug-in.

The minimum POM for the server is:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
                             http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>ball</groupId>
  <artifactId>spring-boot-web-server</artifactId>
  <packaging>jar</packaging>
  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-dependencies</artifactId>
    <version>2.2.1.RELEASE</version>
  </parent>
  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>
  <dependencies verbose="true">
    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
  </dependencies>
</project>

This POM:

  1. Specifies the parent POM as org.springframework.boot:spring-boot-dependencies,1

  2. Sets the ${project.build.sourceEncoding} property to UTF-8 avoid Maven warnings,

  3. Specifies the one required Spring Boot dependency, org.springframework.boot:spring-boot-starter-web, and,

  4. Provides the org.projectlombok:lombok artifact

The last is not strictly required but it saves on writing the Java “boilerplate” required by the implementation beans.

A static, annotated main function must be provided:


And an empty ${project.basedir}/src/main/resources/application.properties:


Executing mvn -B spring-boot:run in the project directory gives the log output:

which indicates Tomcat has been started listening on port 8080. (Note the ASCII-art banner and color in the log lines.) However, browsing to http://localhost:8080/ shows:

This is to be expected! This project has not yet defined any content (static or dynamic) to be served.2

Spring Boot Devtools

The org.springframework.boot:spring-boot-devtools artifact may be added to the classpath to provide developer tools. There are a number of features documented at docs.spring.io including automatic restart and live reload but this article will examine features for setting application properties in a development environment.

The changes to the POM are encapsulated in the spring-boot:run <profile/>:

<?xml version="1.0" encoding="UTF-8"?>
<project ...>
  ...
  <profiles>
    <profile>
      <id>spring-boot:run</id>
      <properties>
        <maven.source.skip>true</maven.source.skip>
        <maven.javadoc.skip>true</maven.javadoc.skip>
        <maven.test.skip>true</maven.test.skip>
      </properties>
      <dependencies>
        <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-devtools</artifactId>
          <scope>runtime</scope>
        </dependency>
      </dependencies>
      <build>
        <defaultGoal>clean spring-boot:run</defaultGoal>
      </build>
    </profile>
  </profiles>
  ...
</project>

This profile:

  1. Sets Maven properties to skip source generation, javadoc generation, and running test.

  2. Sets the Maven default goal(s) to “clean” and “spring-boot:run”.

The Spring Boot Reference Documentation Externalized Configuration section discusses in detail how, where, and when property values may be overridden. Notably ${user.home}/.config/spring-boot/spring-boot-devtools.properties may be used to store a developer’s default settings for all projects and ${project.basedir}/application.properties (typically outside source control) may be used to store properties used for development such as database authentication details.

This author includes the following in his ${user.home}/.config/spring-boot/spring-boot-devtools.properties to reduce log output during testing:

spring.main.banner-mode: OFF
spring.main.headless: false
spring.main.log-startup-info: false

spring.output.ansi.enabled: NEVER

and executing the application with devtools through the new profile (mvn -B -Pspring-boot:run) gives the following shorter and less colorful output.

Spring Boot Actuator

The Spring Boot Actuator provides monitoring and management features. All that is required to enable it is to include its “starter” as a dependency which has been added to the spring-boot:run <profile/>:

<?xml version="1.0" encoding="UTF-8"?>
<project ...>
  ...
  <profiles>
    <profile>
      <id>spring-boot:run</id>
      ...
      <dependencies>
        ...
        <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-actuator</artifactId>
          <scope>runtime</scope>
        </dependency>
        ...
      </dependencies>
      ...
    </profile>
  </profiles>
  ...
</project>

The Spring Boot Actuator also needs to be configured by setting application properties (e.g., in ${user.home}/.config/spring-boot/spring-boot-devtools.properties).

management.server.address: 127.0.0.1
management.server.port: 8081
management.endpoints.web.exposure.include: *
management.endpoints.enabled-by-default: true
management.endpoint.shutdown.enabled: true

Executing mvn -B -Pspring-boot:run gives the following log output:

Which indicates Tomcat is listening on ports 8080 and 8081. http://localhost:8081/actuator returns the following JSON which lists the available endpoints:

{
  "_links": {
    "self": {
      "href": "http://localhost:8081/actuator",
      "templated": false
    },
    "beans": {
      "href": "http://localhost:8081/actuator/beans",
      "templated": false
    },
    "caches": {
      "href": "http://localhost:8081/actuator/caches",
      "templated": false
    },
    "caches-cache": {
      "href": "http://localhost:8081/actuator/caches/{cache}",
      "templated": true
    },
    "health": {
      "href": "http://localhost:8081/actuator/health",
      "templated": false
    },
    "health-path": {
      "href": "http://localhost:8081/actuator/health/{*path}",
      "templated": true
    },
    "info": {
      "href": "http://localhost:8081/actuator/info",
      "templated": false
    },
    "conditions": {
      "href": "http://localhost:8081/actuator/conditions",
      "templated": false
    },
    "shutdown": {
      "href": "http://localhost:8081/actuator/shutdown",
      "templated": false
    },
    "configprops": {
      "href": "http://localhost:8081/actuator/configprops",
      "templated": false
    },
    "env": {
      "href": "http://localhost:8081/actuator/env",
      "templated": false
    },
    "env-toMatch": {
      "href": "http://localhost:8081/actuator/env/{toMatch}",
      "templated": true
    },
    "loggers": {
      "href": "http://localhost:8081/actuator/loggers",
      "templated": false
    },
    "loggers-name": {
      "href": "http://localhost:8081/actuator/loggers/{name}",
      "templated": true
    },
    "heapdump": {
      "href": "http://localhost:8081/actuator/heapdump",
      "templated": false
    },
    "threaddump": {
      "href": "http://localhost:8081/actuator/threaddump",
      "templated": false
    },
    "metrics": {
      "href": "http://localhost:8081/actuator/metrics",
      "templated": false
    },
    "metrics-requiredMetricName": {
      "href": "http://localhost:8081/actuator/metrics/{requiredMetricName}",
      "templated": true
    },
    "scheduledtasks": {
      "href": "http://localhost:8081/actuator/scheduledtasks",
      "templated": false
    },
    "mappings": {
      "href": "http://localhost:8081/actuator/mappings",
      "templated": false
    }
  }
}

For example, http://localhost:8081/actuator/metrics returns:

{
  "names": [
    "jvm.memory.max",
    "jvm.threads.states",
    "process.files.max",
    "jvm.gc.memory.promoted",
    "system.load.average.1m",
    "jvm.memory.used",
    "jvm.gc.max.data.size",
    "jvm.memory.committed",
    "system.cpu.count",
    "logback.events",
    "jvm.buffer.memory.used",
    "tomcat.sessions.created",
    "jvm.threads.daemon",
    "system.cpu.usage",
    "jvm.gc.memory.allocated",
    "tomcat.sessions.expired",
    "jvm.threads.live",
    "jvm.threads.peak",
    "process.uptime",
    "tomcat.sessions.rejected",
    "process.cpu.usage",
    "jvm.classes.loaded",
    "jvm.classes.unloaded",
    "tomcat.sessions.active.current",
    "tomcat.sessions.alive.max",
    "jvm.gc.live.data.size",
    "process.files.open",
    "jvm.buffer.count",
    "jvm.buffer.total.capacity",
    "tomcat.sessions.active.max",
    "process.start.time"
  ]
}

And “process start time” can be retrieved through http://localhost:8081/actuator/metrics/process.start.time which returns something like:

{
  "name": "process.start.time",
  "description": "Start time of the process since unix epoch.",
  "baseUnit": "seconds",
  "measurements": [
    {
      "statistic": "VALUE",
      "value": 1573860137.896
    }
  ],
  "availableTags": []
}

Summary

This article demonstrates the use of the spring-boot-starter-web, the use of spring-boot-devtools, and the integration of spring-boot-starter-actuator. The POM defines a spring-boot:run <profile/> which can be used for testing the application. The projects described in subsequent articles in this series will benefit by having both Spring Boot Devtools and the Spring Boot Actuator activated during development runs of the application.

Part 2 of this series demonstrates how static resources may be added to be served by the web server.


  1. This author prefers this specific method if only because versions of provided dependencies may be updated simply by updating the corresponding property value in the project POM.

  2. In fact, serving content is not covered here and will be the subject of subsequent articles in this series.