Understanding Spring Boot Configuration Metadata
When developing reusable Spring Boot libraries, providing proper configuration metadata ensures that developers using your library get full IDE support including auto-completion, documentation tooltips, and validation warnings. This metadata describes the custom properties your library exposes through @ConfigurationProperties.
Library vs Application Metadata
Spring Boot distinguishes between two types of metadata files:
spring-configuration-metadata.json- Generated automatically during compilation for the current project's configuration classesadditional-spring-configuration-metadata.json- Manual created to supplement metadata from third-party libraries or for properties not discovered by annotation processing
For library authors, the key insight is that the automatically generated file only applies to the project where the build occurs. When business applications consume your library, they won't see your configuration properties unless you package the metadata correct.
Annotation Processing Mechanism
Spring Boot's annotation processor scans classes annotated with @ConfigurationProperties during the Maven or Gradle build. It extracts property details from field declarations, getter/setter methods, and supporting annotations like @DefaultValue or @DeprecatedConfigurationProperty.
The processor generates META-INF/spring-configuration-metadata.json in the compiled output, which must be included in your library's JAR file for downstream IDE support.
Implementation Example
Consider a library providing external API client settings:
package org.sample.lib.connect;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.bind.DefaultValue;
@ConfigurationProperties(prefix = "api-client")
public class ApiClientProperties {
private Connection connection = new Connection();
private Retry retry = new Retry();
public Connection getConnection() { return connection; }
public void setConnection(Connection connection) { this.connection = connection; }
public Retry getRetry() { return retry; }
public void setRetry(Retry retry) { this.retry = retry; }
public static class Connection {
private String baseUrl;
private int port;
public String getBaseUrl() { return baseUrl; }
public void setBaseUrl(String baseUrl) { this.baseUrl = baseUrl; }
public int getPort() { return port; }
public void setPort(int port) { this.port = port; }
}
public static class Retry {
@DefaultValue("3")
private int maxAttempts;
private long backoffDelay;
public int getMaxAttempts() { return maxAttempts; }
public void setMaxAttempts(int maxAttempts) { this.maxAttempts = maxAttempts; }
public long getBackoffDelay() { return backoffDelay; }
public void setBackoffDelay(long backoffDelay) { this.backoffDelay = backoffDelay; }
}
}
After compilation, the generated metadata appears as:
{
"groups": [
{
"name": "api-client",
"type": "org.sample.lib.connect.ApiClientProperties",
"sourceType": "org.sample.lib.connect.ApiClientProperties"
},
{
"name": "api-client.connection",
"type": "org.sample.lib.connect.ApiClientProperties$Connection",
"sourceType": "org.sample.lib.connect.ApiClientProperties",
"sourceMethod": "getConnection()"
},
{
"name": "api-client.retry",
"type": "org.sample.lib.connect.ApiClientProperties$Retry",
"sourceType": "org.sample.lib.connect.ApiClientProperties",
"sourceMethod": "getRetry()"
}
],
"properties": [
{
"name": "api-client.connection.base-url",
"type": "java.lang.String",
"sourceType": "org.sample.lib.connect.ApiClientProperties$Connection"
},
{
"name": "api-client.connection.port",
"type": "java.lang.Integer",
"sourceType": "org.sample.lib.connect.ApiClientProperties$Connection"
},
{
"name": "api-client.retry.max-attempts",
"type": "java.lang.Integer",
"sourceType": "org.sample.lib.connect.ApiClientProperties$Retry",
"defaultValue": 3
},
{
"name": "api-client.retry.backoff-delay",
"type": "java.lang.Long",
"sourceType": "org.sample.lib.connect.ApiClientProperties$Retry"
}
],
"hints": []
}
This metadata, when packaged in you're library's JAR, enables IDEs to provide intelligent assistance for properties like api-client.connection.base-url and api-client.retry.max-attempts in consuming applications.