Beginner Reading time: ~8 min

Configuration

application.properties, application.yml, @Value, @ConfigurationProperties, config precedence

Configuration

1. Definition

Spring Boot configuration is the system for managing external settings (properties, profiles, secrets). Spring Boot follows the externalized configuration principle: application behavior is controlled from sources outside the code (property files, environment variables, command-line arguments).

The two main configuration formats:

  • application.properties — simple key-value pairs
  • application.yml — YAML hierarchy (indentation-based)
# application.properties
server.port=8081
spring.datasource.url=jdbc:postgresql://localhost/mydb
# application.yml
server:
  port: 8081
spring:
  datasource:
    url: jdbc:postgresql://localhost/mydb

2. Core Concepts

Property source hierarchy

Spring Boot reads configuration from 17+ sources. Higher priority overrides lower:

  1. Command-line arguments (--server.port=9090)
  2. SPRING_APPLICATION_JSON (inline JSON)
  3. Servlet/JNDI parameters
  4. OS environment variables (SERVER_PORT=9090)
  5. Profile-specific property files (application-prod.properties)
  6. Application property files (application.properties)
  7. @PropertySource annotation
  8. Default properties (SpringApplication.setDefaultProperties)

@Value annotation

Injecting individual properties:

@Component
public class MyComponent {
    @Value("${server.port}")
    private int port;

    @Value("${app.name:DefaultApp}")
    private String appName; // default value after colon

    @Value("${app.features.enabled:false}")
    private boolean featuresEnabled;
}

@ConfigurationProperties

Type-safe property binding to a POJO:

@ConfigurationProperties(prefix = "app.datasource")
public class DataSourceProperties {
    private String url;
    private String username;
    private String password;
    private int maxPoolSize = 10;

    // getters and setters
    public String getUrl() { return url; }
    public void setUrl(String url) { this.url = url; }
    public String getUsername() { return username; }
    public void setUsername(String username) { this.username = username; }
    public String getPassword() { return password; }
    public void setPassword(String password) { this.password = password; }
    public int getMaxPoolSize() { return maxPoolSize; }
    public void setMaxPoolSize(int maxPoolSize) { this.maxPoolSize = maxPoolSize; }
}

Profiles

Environment-specific configuration:

# application-dev.properties
server.port=8080
spring.datasource.url=jdbc:h2:mem:devdb

# application-prod.properties
server.port=443
spring.datasource.url=jdbc:postgresql://prod-host/mydb

Activation: spring.profiles.active=dev or --spring.profiles.active=prod.

3. Practical Usage

Property file locations

src/main/resources/
├── application.properties          ← default
├── application.yml                 ← alternative (YAML)
├── application-dev.properties      ← dev profile
├── application-prod.properties     ← prod profile
└── application-test.properties     ← test profile

YAML advantages and disadvantages

# Hierarchical structure — more readable
spring:
  datasource:
    url: jdbc:postgresql://localhost/mydb
    username: admin
    password: secret
  jpa:
    hibernate:
      ddl-auto: update
    show-sql: true
  • ✅ Hierarchical, less repetition
  • ✅ Native list support
  • ❌ Indentation-sensitive (tab vs space errors)
  • ❌ @PropertySource does not support YAML

@ConfigurationProperties activation

@Configuration
@EnableConfigurationProperties(DataSourceProperties.class)
public class AppConfig {
}

// OR: @ConfigurationPropertiesScan on the application class
@SpringBootApplication
@ConfigurationPropertiesScan
public class MyApp {
    public static void main(String[] args) {
        SpringApplication.run(MyApp.class, args);
    }
}

Property validation

@ConfigurationProperties(prefix = "app.mail")
@Validated
public class MailProperties {
    @NotBlank
    private String host;

    @Min(1) @Max(65535)
    private int port = 587;

    @Email
    private String from;

    // getters and setters
    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 String getFrom() { return from; }
    public void setFrom(String from) { this.from = from; }
}

4. Code Examples

@Value vs @ConfigurationProperties

// @Value — individual properties, simple cases
@Component
public class SimpleConfig {
    @Value("${app.name}")
    private String appName;

    @Value("${app.timeout:5000}")
    private long timeout;
}

// @ConfigurationProperties — group binding, type-safe
@ConfigurationProperties(prefix = "app")
public class AppProperties {
    private String name;
    private long timeout = 5000;
    private final Security security = new Security();

    public static class Security {
        private String secret;
        private long tokenExpiry = 3600;
        // getters + setters
        public String getSecret() { return secret; }
        public void setSecret(String s) { this.secret = s; }
        public long getTokenExpiry() { return tokenExpiry; }
        public void setTokenExpiry(long t) { this.tokenExpiry = t; }
    }

    // getters + setters
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    public long getTimeout() { return timeout; }
    public void setTimeout(long timeout) { this.timeout = timeout; }
    public Security getSecurity() { return security; }
}

Relaxed binding

Spring Boot uses relaxed binding:

# All of these refer to the same property:
app.datasource.max-pool-size=10   # kebab-case (recommended)
app.datasource.maxPoolSize=10     # camelCase
app.datasource.max_pool_size=10   # underscore
APP_DATASOURCE_MAX_POOL_SIZE=10   # UPPER_CASE (env var)

Profile-specific beans

@Configuration
public class DataSourceConfig {

    @Bean
    @Profile("dev")
    public DataSource devDataSource() {
        return new EmbeddedDatabaseBuilder()
            .setType(EmbeddedDatabaseType.H2)
            .build();
    }

    @Bean
    @Profile("prod")
    public DataSource prodDataSource() {
        HikariDataSource ds = new HikariDataSource();
        ds.setJdbcUrl("jdbc:postgresql://prod-host/mydb");
        return ds;
    }
}

Environment variable → property mapping

# Shell / Docker
export SPRING_DATASOURCE_URL=jdbc:postgresql://prod/mydb
export SERVER_PORT=9090
export APP_SECURITY_SECRET=my-secret-key

Mapping rule: UPPER_CASE + underscore → lower.case + dot.

5. Trade-offs

Aspect @Value @ConfigurationProperties
Simplicity ✅ Quick, single property ❌ More boilerplate
Type safety ❌ String-based, runtime errors ✅ Compile-time types
Validation ❌ No built-in ✅ @Validated + JSR-303
IDE support ❌ Limited ✅ Autocomplete (metadata)
Grouping ❌ Scattered ✅ Hierarchical POJO
SpEL ✅ Supported ❌ Not supported

Properties vs YAML

Aspect .properties .yml
Format Key=value Hierarchical
Repetition Full prefix on every line No repetition
Lists list[0]=a, list[1]=b - a + - b
@PropertySource ✅ Supported ❌ Not supported
Error risk Low Indentation sensitive

6. Common Mistakes

❌ @Value SpEL error

// WRONG: # instead of $ → SpEL, not property
@Value("#{server.port}")  // ← SpEL
private int port;

// CORRECT:
@Value("${server.port}")  // ← property placeholder
private int port;

❌ Missing property, no default

// WRONG: if property doesn't exist → startup error
@Value("${app.missing.property}")
private String value;

// CORRECT: default value
@Value("${app.missing.property:fallback}")
private String value;

❌ YAML indentation error

# WRONG: tab character in YAML
spring:
    datasource:    # ← TAB was here! Error!
    url: jdbc:...

# CORRECT: spaces
spring:
  datasource:
    url: jdbc:...

❌ Missing @ConfigurationProperties registration

// WRONG: properties class not registered → properties are null
@ConfigurationProperties(prefix = "app")
public class AppProps { ... }

// CORRECT: @EnableConfigurationProperties OR @ConfigurationPropertiesScan
@Configuration
@EnableConfigurationProperties(AppProps.class)
public class Config { }

❌ Misunderstanding property override order

Command-line arguments (--server.port=9090) always override application.properties values. Developers sometimes don't understand why the file-based value doesn't take effect.

7. Deep Dive

Config file search order

Spring Boot searches for config files in these locations:

  1. file:./config/ (project root /config folder)
  2. file:./config/*/ (subdirectories)
  3. file:./ (project root)
  4. classpath:/config/ (classpath /config)
  5. classpath:/ (classpath root)

spring.config.import (Boot 2.4+)

# Import external files
spring.config.import=optional:file:/etc/myapp/config.properties
spring.config.import=optional:configserver:http://config-server:8888

@ConfigurationProperties metadata

Generate metadata for IDE autocomplete:

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

This generates a META-INF/spring-configuration-metadata.json file.

Immutable @ConfigurationProperties (record)

@ConfigurationProperties(prefix = "app.mail")
public record MailProperties(
    @NotBlank String host,
    @DefaultValue("587") int port,
    @Email String from
) {}

Constructor binding: no setters needed, the record is immutable.

Secret management

# NEVER commit secrets!
# Use environment variables:
spring.datasource.password=${DB_PASSWORD}

# Or Spring Cloud Config + Vault
spring.config.import=vault://secret/myapp

8. Interview Questions

  1. What is the difference between application.properties and application.yml? Properties: key=value, YAML: hierarchical indentation. Properties supports @PropertySource, YAML doesn't. YAML is more readable but indentation-sensitive.

  2. What is the property source priority order? Command line > env vars > profile-specific file > application.properties > defaults. Higher priority overrides lower.

  3. When should you use @Value vs @ConfigurationProperties? @Value: individual properties, SpEL needed. @ConfigurationProperties: group binding, validation, IDE autocomplete, hierarchical configuration.

  4. What is relaxed binding? Spring Boot automatically understands different formats: max-pool-size = maxPoolSize = MAX_POOL_SIZE.

  5. How do you validate configuration properties? @Validated + JSR-303 annotations (@NotBlank, @Min, @Max, @Email) on the @ConfigurationProperties class.

  6. What is spring.config.import? Boot 2.4+ feature: imports external files, config server, vault into the property source.

  7. How do you handle secrets? Environment variables, Spring Cloud Config + Vault, Kubernetes secrets — never commit to property files.

9. Glossary

Term Meaning
Property source Source of configuration values (file, env var, command line)
@Value Individual property injection with SpEL support
@ConfigurationProperties Type-safe, grouped property binding
Relaxed binding Automatic format recognition (kebab, camel, upper)
Profile Environment-specific configuration (dev, prod, test)
BOM Bill of Materials — version management
spring.config.import External configuration import (Boot 2.4+)
Configuration processor Annotation processor for IDE metadata generation
Constructor binding Immutable @ConfigurationProperties (record, final fields)
Externalized configuration Controlling app behavior from outside the code

10. Cheatsheet

# Property source priority (decreasing):
# 1. --command-line
# 2. SPRING_APPLICATION_JSON
# 3. OS env var (SERVER_PORT=9090)
# 4. application-{profile}.properties
# 5. application.properties
# 6. @PropertySource
# 7. SpringApplication.setDefaultProperties

# Profile activation
spring.profiles.active=dev,metrics

# Config import (Boot 2.4+)
spring.config.import=optional:file:/etc/myapp/config.properties

# Relaxed binding
app.max-pool-size=10   # kebab (recommended in .properties)
APP_MAX_POOL_SIZE=10   # UPPER (env var)
// @Value — individual, SpEL
@Value("${app.name:default}")
private String name;

// @ConfigurationProperties — grouped, type-safe
@ConfigurationProperties(prefix = "app")
@Validated
public class AppProps {
    @NotBlank private String name;
    @Min(1) private int port;
}

// Record binding (immutable)
@ConfigurationProperties(prefix = "app")
public record AppProps(@NotBlank String name, @DefaultValue("8080") int port) {}

🎼 Games

10 questions