Are you also struggling keeping the documentation of your Spring configuration properties in line with the code? In this blog, you will take a look at the Spring Configuration Property Documenter Maven plugin, which solves this issue for you. Enjoy!

1. Introduction

Almost every Spring (Boot) application makes use of configuration properties. These configuration properties ensure that certain items in your application can be configured by means of an application.properties (or yaml) file. However, there is also a need to document these properties in order that someone knows what these properties mean, how to use them, etc. This is often documented in a README file. This README file needs to be manually maintained while the properties are present in a Java class which also contains documentation and annotations.

Wouldn’t it be great when the documentation was present at one location (the Java class, close to the code) and the documentation could be generated out of the code? Good news! This is exactly what the Spring Configuration Property Documenter Maven plugin will do for you! In the remainder of this post, you will explore some of the features of this Maven plugin and how you can easily incorporate it in your project. The official documentation is more elaborate and can be found here.

The sources used in this blog, can be found at GitHub.

2. Sample Application

First of all, you need to create a basic sample application. Navigate to Spring Initializr and select the dependencies Spring Web, Lombok and Spring Configuration Processor.

Annotate the main Spring Boot application class with @ConfigurationPropertiesScan.

@SpringBootApplication
@ConfigurationPropertiesScan("com.mydeveloperplanet.myspringconfigdocplanet.config")
public class MySpringConfigDocPlanetApplication {

	public static void main(String[] args) {
		SpringApplication.run(MySpringConfigDocPlanetApplication.class, args);
	}

}

Create a configuration class MyFirstProperties in package config. The configuration class makes use of constructor binding. See also a previous post Spring Boot Configuration Properties Explained for more information about the different ways to create configuration properties.

@Getter
@ConfigurationProperties("my.first.properties")
public class MyFirstProperties {

    private final String stringProperty;

    private final boolean booleanProperty;

    public MyFirstProperties(String stringProperty, boolean booleanProperty) {
        this.stringProperty = stringProperty;
        this.booleanProperty = booleanProperty;
    }
}

Also, a ConfigurationController is added in package controller which returns the properties. This controller is only added as an example how to use the properties, it will not be of any relevance for this blog.

Build the application.

$ mvn clean verify

Run the application.

$ mvn spring-boot:run

Invoke the endpoint as configured in the ConfigurationController.

$ curl http://localhost:8080/configuration

Take a look at directory target/classes/META-INF. A file spring-configuration-metadata.json is present here which contains metadata about the configuration classes. This information is used by the Spring Configuration Property Documenter Maven plugin in order to generate the documentation.

This metadata file is generated because you added the Spring Configuration Processor as a dependency.

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-configuration-processor</artifactId>
  <optional>true</optional>
</dependency>

3. Generate Documentation

The plugin is able to generate the documentation in four different formats:

  • ASCII Doctor
  • Markdown
  • HTML
  • XML

In order to generate the documentation, you only have to add the plugin to the build section (next to adding the Spring Configuration Processor dependency). For each format type, an execution is added. If you only want documentation in markdown, just remove the other executions.

<build>
  <plugins>
    <plugin>
      <groupId>org.rodnansol</groupId>
	  <artifactId>spring-configuration-property-documenter-maven-plugin</artifactId>
	  <version>0.6.1</version>
      <executions>
        <execution>
          <id>generate-adoc</id>
          <phase>process-classes</phase>
          <goals>
		    <goal>generate-property-document</goal>
          </goals>
          <configuration>
		    <type>ADOC</type>
          </configuration>
        </execution>
        <execution>
          <id>generate-markdown</id>
          <phase>process-classes</phase>
          <goals>
		    <goal>generate-property-document</goal>
          </goals>
          <configuration>
		    <type>MARKDOWN</type>
          </configuration>
        </execution>
        <execution>
          <id>generate-html</id>
          <phase>process-classes</phase>
          <goals>
		    <goal>generate-property-document</goal>
          </goals>
          <configuration>
		    <type>HTML</type>
          </configuration>
        </execution>
        <execution>
          <id>generate-xml</id>
          <phase>process-classes</phase>
          <goals>
		    <goal>generate-property-document</goal>
          </goals>
          <configuration>
		    <type>XML</type>
          </configuration>
        </execution>
      </executions>
    </plugin>
  </plugins>
</build>

The documentation will be generated when executing a build with Maven, but a quick way is to execute the process-classes goal.

$ mvn process-classes

Or you can invoke a specific execution.

$ mvn spring-configuration-property-documenter:generate-property-document@generate-markdown

Take a look at directory target/property-docs. For each type, documentation for the configuration properties are added.

The ASCII Doctor format.

The Markdown format.

The HTML format.

The XML format is a bit elaborate to display, but it contains an XML representation of the above.

In case you have a Maven multi-module project, you can combine all the properties of the different modules into one file. How to do so is described in the documentation.

4. Customize Output

In the remainder of the post, you will continue with the markdown format. In the above screenshots, you notice that an Unknown Group is added. This group is also empty. By default, this group is always added to the output, but it is possible to remove it by means of the markdownCustomization parameter. There are many more customizations available which are listed in the documentation. In order to disable the Unknown Group in the output, you set the parameter includedUnknownGroup to false.

<execution>
  <id>generate-markdown</id>
  <phase>process-classes</phase>
  <goals>
    <goal>generate-property-document</goal>
  </goals>
  <configuration>
    <type>MARKDOWN</type>
      <markdownCustomization>
        <includeUnknownGroup>false</includeUnknownGroup>
      </markdownCustomization>
  </configuration>
</execution>

Execute the markdown generation, and you will notice that the Unknown Group is not present anymore in the output.

5. Description and Default Value

In the above output, you notice that the description of the properties and the default value of stringProperty is empty.

Create a new configuration class MySecondProperties. Add a Javadoc above the fields representing the properties and add a @DefaultValue annotation before stringProperty in the constructor.

@Getter
@ConfigurationProperties("my.second.properties")
public class MySecondProperties {

    /**
     * This is the description for stringProperty
     */
    private final String stringProperty;

    /**
     * This is the description for booleanProperty
     */
    private final boolean booleanProperty;

    public MySecondProperties(@DefaultValue("default value for stringProperty") String stringProperty,
                              boolean booleanProperty) {
        this.stringProperty = stringProperty;
        this.booleanProperty = booleanProperty;
    }
}

Generate the documentation and you will notice that the description is present and the default value is filled for stringProperty.

This is quite awesome, isn’t it? The documentation is right there together with the code and the documentation in markdown is generated from it.

6. Nested Properties

Does it also work for nested properties? Let’s find out. Create a configuration class MyThirdProperties with a nested property nestedProperty which also contains a stringProperty and a booleanProperty. The booleanProperty is defaulted to true.

@Getter
@ConfigurationProperties("my.third.properties")
public class MyThirdProperties {

    /**
     * This is the description for stringProperty
     */
    private final String stringProperty;

    /**
     * This is the description for booleanProperty
     */
    private final boolean booleanProperty;

    private final NestedProperty nestedProperty;

    public MyThirdProperties(@DefaultValue("default value for stringProperty") String stringProperty,
                             boolean booleanProperty,
                             @DefaultValue NestedProperty nestedProperty) {
        this.stringProperty = stringProperty;
        this.booleanProperty = booleanProperty;
        this.nestedProperty = nestedProperty;
    }

    @Getter
    public static class NestedProperty {

        /**
         * This is the description for nested stringProperty
         */
        private final String stringProperty;

        /**
         * This is the description for nested booleanProperty
         */
        private final boolean booleanProperty;

        public NestedProperty(@DefaultValue("default value for nested stringProperty") String stringProperty,
                              @DefaultValue("true") boolean booleanProperty) {
            this.stringProperty = stringProperty;
            this.booleanProperty = booleanProperty;
        }
    }
}

Generate the markdown documentation and you will notice that the documentation also contains the nested property.

7. Records

Since the configuration properties are immutable, it is even better to use Java records instead of using Lombok. Create a configuration class MyFourthProperties and use Java records. The question is where to add the description of the properties because there are no fields where you can add Javadoc to.

/**
 * @param stringProperty This is the description for stringProperty
 * @param booleanProperty This is the description for booleanProperty
 */
@ConfigurationProperties("my.fourth.properties")
public record MyFourthProperties (@DefaultValue("default value for stringProperty") String stringProperty,
                                  @DefaultValue("true") boolean booleanProperty) {
}

Generate the markdown documentation and notice that the description is empty.

This is not an issue with the plugin, however. The description is empty in the spring-configuration-metadata.json file and the plugin just uses this information.

A question on Stack Overflow is asked about this. Hopefully an answer will follow.

8. Conclusion

The Spring Configuration Property Documenter Maven plugin is a great initiative in order to keep documentation closer to the code and to generate it based on the code. It fills a gap in my opinion which benefits almost all Spring projects.