By WebNest Studio Engineering Team
Spring Boot Tutorial
Spring Boot Configuration Properties
Spring Boot applications are configured externally rather than by editing code — you set values in application.properties or application.yml (or environment variables, command-line arguments, and more), and Spring Boot binds them into your running application. This lets the exact same jar behave differently in development, staging, and production without a rebuild.
application.properties vs application.yml
Both files live in src/main/resources and serve the same purpose — key-value configuration — but with different syntax. Properties files use dot-separated flat keys (server.port=8081); YAML uses nested indentation, which reads better for deeply structured configuration. You can use either, but not both for the same key in the same profile (if both exist, .properties takes precedence over .yml in default Spring Boot resolution).
Injecting Individual Values with @Value
For a single simple value, @Value("${property.name}") injects it directly into a field. This works well for one-off values but becomes unwieldy once you have more than a few related settings, since each one needs its own annotation and there's no compile-time grouping or validation.
Type-Safe Binding with @ConfigurationProperties
For a related group of settings, @ConfigurationProperties binds an entire prefix of properties onto a strongly typed Java class in one step, with support for nested objects, lists, and maps. The class must be registered as a bean, typically via @ConfigurationProperties combined with @Component, or explicitly with @EnableConfigurationProperties on a configuration class.
Immutable Settings with a Record
A Java record is the cleanest way to hold settings: the values are bound once through the constructor, and nothing can change them afterwards. A record needs no setters and no @Component. Instead, add @ConfigurationPropertiesScan to the main application class, and Spring Boot finds every @ConfigurationProperties class in your packages. @DefaultValue on a component supplies a value when the property is missing.
Durations, Sizes and Lists
Spring Boot converts readable strings into proper types. A Duration field accepts 30s, 5m or 2h, a DataSize field accepts 10MB, and a List<String> binds from a YAML list or from a comma-separated value. This is much safer than an int timeoutMillis, where nobody can tell whether 30 means seconds or milliseconds.
Failing Fast with @Validated
Add @Validated to the class and Bean Validation annotations to its fields, and Spring Boot checks the values at startup. A bad value, such as a port of 70000, stops the application with a message naming the property, the value, where it came from and the rule it broke. Failing at startup is far better than failing at 3 a.m. when the first email is sent.
Where Values Can Come From
The same property can be set in several places, and the more specific one wins. From lowest to highest priority, the common sources are: application.properties or .yml inside the jar, profile-specific files, environment variables, and command-line arguments. Environment variables use relaxed binding: APP_MAIL_PORT=2525 overrides app.mail.port, which is how containers and cloud platforms usually pass configuration.
Examples
Binding a group of related properties with @ConfigurationProperties
# application.yml
app:
mail:
host: smtp.webnest.com
port: 587
retry-attempts: 3
---
@ConfigurationProperties(prefix = "app.mail")
@Component
public class MailProperties {
private String host;
private int port;
private int retryAttempts;
// getters and setters required for binding
public String getHost() { return host; }
public void setHost(String host) { this.host = host; }
public int getPort() { return port; }
public void setPort(int port) { this.port = port; }
public int getRetryAttempts() { return retryAttempts; }
public void setRetryAttempts(int retryAttempts) { this.retryAttempts = retryAttempts; }
}
@Service
public class MailService {
private final MailProperties properties;
public MailService(MailProperties properties) {
this.properties = properties;
}
public void printConfig() {
System.out.println(properties.getHost() + ":" + properties.getPort()
+ " retries=" + properties.getRetryAttempts());
}
}
smtp.webnest.com:587 retries=3
An immutable, validated settings record with defaults, a Duration, a DataSize and a list (tested on Spring Boot 4.1.1)
# application.yml
app:
mail:
host: smtp.webnest.dev
port: 587
timeout: 30s
admin-emails:
- ops@webnest.dev
- asha@webnest.dev
// MailProperties.java
@Validated
@ConfigurationProperties(prefix = "app.mail")
public record MailProperties(
@NotBlank String host,
@Min(1) @Max(65535) int port,
@DefaultValue("3") int retryAttempts,
@DefaultValue("10s") Duration timeout,
@DefaultValue("10MB") DataSize maxAttachmentSize,
List<String> adminEmails) {
}
// App.java
@SpringBootApplication
@ConfigurationPropertiesScan // registers every @ConfigurationProperties class
public class App {
public static void main(String[] args) { SpringApplication.run(App.class, args); }
}
// MailReport.java
@Component
public class MailReport implements CommandLineRunner {
private final MailProperties mail;
public MailReport(MailProperties mail) {
this.mail = mail;
}
@Override
public void run(String... args) {
System.out.println(mail.host() + ":" + mail.port() + " retries=" + mail.retryAttempts()
+ " timeout=" + mail.timeout().toSeconds() + "s"
+ " maxAttachment=" + mail.maxAttachmentSize().toMegabytes() + "MB"
+ " admins=" + mail.adminEmails());
}
}
$ java -jar app.jar
smtp.webnest.dev:587 retries=3 timeout=30s maxAttachment=10MB admins=[ops@webnest.dev, asha@webnest.dev]
$ APP_MAIL_PORT=2525 java -jar app.jar
smtp.webnest.dev:2525 retries=3 timeout=30s maxAttachment=10MB admins=[ops@webnest.dev, asha@webnest.dev]
$ java -jar app.jar --app.mail.port=70000
***************************
APPLICATION FAILED TO START
***************************
Description:
Binding to target demo.cfg.MailProperties failed:
Property: app.mail.port
Value: "70000"
Origin: "app.mail.port" from property source "commandLineArgs"
Reason: must be less than or equal to 65535
Common Mistakes
- Storing durations as plain numbers (timeoutMillis = 30000) instead of a Duration, so every reader has to guess the unit.
- Leaving settings unvalidated, so a typo in production is only discovered when the code that uses the value finally runs.
- Putting secrets such as SMTP passwords in application.yml inside the repository instead of supplying them through environment variables or a secrets manager.
- Forgetting getters/setters on a @ConfigurationProperties class, which silently breaks binding since Spring uses standard JavaBean conventions.
- Mixing kebab-case, camelCase, and snake_case inconsistently — Spring Boot's relaxed binding accepts several forms, but readability suffers without a consistent style (kebab-case is conventional in YAML).
- Using @Value scattered across many classes for what is really one configuration group, instead of consolidating into a single @ConfigurationProperties class.
- Not adding spring-boot-configuration-processor, which means the IDE has no autocomplete or metadata for custom properties.
Exercise
Using the lesson's Binding a group of related properties with @ConfigurationProperties example, create a small implementation or design sketch that demonstrates application.properties and application.yml both configure the app externally; .properties wins over .yml if both define the same key.. Add a test case or scenario that would catch Storing durations as plain numbers (timeoutMillis = 30000) instead of a Duration, so every reader has to guess the unit., and describe the expected result.
Show hints
- Start with application.properties vs application.yml and compare your approach with Binding a group of related properties with @ConfigurationProperties. Identify the decision or behavior the lesson recommends.
- Walk through your scenario step by step. Confirm that it follows application.properties and application.yml both configure the app externally; .properties wins over .yml if both define the same key. and that it would catch the failure described by Storing durations as plain numbers (timeoutMillis = 30000) instead of a Duration, so every reader has to guess the unit..
Quick quiz
1. Which choice is a mistake to avoid in Spring Boot Configuration Properties?
- A. Leaving settings unvalidated, so a typo in production is only discovered when the code that uses the value finally runs.
- B. application.properties and application.yml both configure the app externally; .properties wins over .yml if both define the same key.
- C. Storing durations as plain numbers (timeoutMillis = 30000) instead of a Duration, so every reader has to guess the unit.
Show answer
C. Avoid this approach: Storing durations as plain numbers (timeoutMillis = 30000) instead of a Duration, so every reader has to guess the unit. The lesson recommends: application.properties and application.yml both configure the app externally; .properties wins over .yml if both define the same key.
Interview questions
A teammate is about to make this mistake: Storing durations as plain numbers (timeoutMillis = 30000) instead of a Duration, so every reader has to guess the unit. How would you diagnose and correct it using the lesson's guidance?
Show answer
application.properties and application.yml both configure the app externally; .properties wins over .yml if both define the same key. Apply this in application.properties vs application.yml; the example Binding a group of related properties with @ConfigurationProperties shows one way to do it. Avoid Storing durations as plain numbers (timeoutMillis = 30000) instead of a Duration, so every reader has to guess the unit. and explain how your implementation or design prevents that failure.
Key Points to Remember
- application.properties and application.yml both configure the app externally; .properties wins over .yml if both define the same key.
- @Value injects single values; @ConfigurationProperties binds a whole related group into a typed class.
- A JavaBean-style @ConfigurationProperties class needs getters and setters; a record binds through its constructor instead. Either way it must be registered as a bean.
- Externalized configuration lets the same build run correctly in different environments.
- A record with @ConfigurationPropertiesScan gives immutable settings without setters or @Component.
- @Validated plus constraint annotations makes bad configuration fail at startup with a clear message.
- Environment variables (APP_MAIL_PORT) and command-line arguments override values from application files.
Practice in the playground
Write and run your own code for this topic, or start from the exercise or an example. Runs plain Java 17. Frameworks such as Spring, and databases, are not available here. Your code for each topic is kept in this browser.
Run the examples
Change an input, predict the result, then compare it with the output. Explain why the result changes.
These examples need a JDK project with their libraries on your computer, so they can be copied but not run here. Codelab runs Python, FastAPI backends with SQLite, plain Java 17 and HTML/CSS/JavaScript.
Written by
WebNest Studio Engineering Team
Practical lessons from developers working with Java, Python, React, databases and production systems.
Led by Vansh Duggal, Founder, WebNest Studio · Full-stack & software engineering