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

allen-ball

Allen D. Ball

Posted on November 18, 2019

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

(This post has been updated at blog.hcf.dev with a later version of Spring Boot.)

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.3.0.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>
Enter fullscreen mode Exit fullscreen mode

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:

package application;

import lombok.NoArgsConstructor;
import lombok.ToString;
import lombok.extern.log4j.Log4j2;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@NoArgsConstructor @ToString @Log4j2
public class Launcher {
    public static void main(String[] argv) throws Exception {
        SpringApplication application = new SpringApplication(Launcher.class);

        application.run(argv);
    }
}
Enter fullscreen mode Exit fullscreen mode

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

# application.properties
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

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"
  ]
}
Enter fullscreen mode Exit fullscreen mode

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": []
}
Enter fullscreen mode Exit fullscreen mode

Spring Boot Properties Migrator

Spring Boot relies on numerous properties whose names may change from release to release. The spring-boot-properties-migrator is configured in 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-properties-migrator</artifactId>
          <scope>runtime</scope>
        </dependency>
        ...
      </dependencies>
      ...
    </profile>
  </profiles>
  ...
</project>
Enter fullscreen mode Exit fullscreen mode

In the event that a property name changes it will be noted in the log output:

Summary

This article demonstrates the use of the spring-boot-starter-web, the use of spring-boot-devtools,3 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.

[3] With spring-boot-properties-migrator.

💖 💪 🙅 🚩
allen-ball
Allen D. Ball

Posted on November 18, 2019

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related