PrepZone Logo
PrepZone

Properties, YAML, and Profiles

Externalised configuration, environment profiles, and binding typed properties.

Why this matters

  • Hardcoding database URLs or API keys in Java forces recompilation for every environment change.
  • Profiles activate bean sets and property files per environment — dev uses H2, prod uses PostgreSQL.
  • @ConfigurationProperties binds typed configuration objects, replacing scattered @Value injections.
Classpath scanFind @AutoConfiguration classes
@Conditional checksOnClass, OnBean, OnProperty
Register beansOnly if conditions pass
Boot checks classpath and existing beans before creating defaults.

application.yml structure

Java
spring:
  application:
    name: bookstore-api
  datasource:
    url: jdbc:postgresql://127.0.0.1:5432/bookstore
    username: bookstore
    password: ${DB_PASSWORD:localdev}

server:
  port: 8080

bookstore:
  pricing:
    service-url: https://pricing.internal.example.com
    timeout-ms: 3000
  catalog:
    page-size: 20

Spring binds spring.* to built-in properties and custom bookstore.* keys to your @ConfigurationProperties class.

Typed configuration properties

Java
@ConfigurationProperties(prefix = "bookstore.pricing")
public record PricingProperties(
    String serviceUrl,
    int timeoutMs
) {}

@Configuration
@EnableConfigurationProperties(PricingProperties.class)
public class BookStoreConfig { }

Injection into services:

Java
@Service
public class PricingService {
    private final RestClient client;

    public PricingService(PricingProperties props) {
        this.client = RestClient.builder()
            .baseUrl(props.serviceUrl())
            .build();
    }
}

Records make immutable config objects with validation support via @Validated and @NotBlank.

Profiles

Activate environment-specific configuration:

Java
# application-dev.yml
spring:
  datasource:
    url: jdbc:h2:mem:bookstore
  h2:
    console:
      enabled: true

logging:
  level:
    com.example.bookstore: DEBUG
Java
# application-prod.yml
spring:
  datasource:
    url: jdbc:postgresql://${DB_HOST}:5432/bookstore

Activate with:

Java
java -jar bookstore-api.jar --spring.profiles.active=prod

Or in application.yml:

Java
spring:
  profiles:
    active: dev

Profile strategies

  • application-{profile}.yml — Property overrides per profile.
  • @Profile("dev") on beans — Register beans only in matching environments.
  • spring.profiles.active — Comma-separated list; later profiles override earlier ones.

Profile-specific beans

Java
@Configuration
@Profile("dev")
public class DevDataConfig {
    @Bean
    public CommandLineRunner seedBooks(BookRepository repo) {
        return args -> {
            repo.save(new Book("The Pragmatic Programmer", "978-0201616224", 42.99));
            repo.save(new Book("Clean Code", "978-0132350884", 38.50));
        };
    }
}

The seeder runs only in dev — production starts with an empty catalog controlled by migrations.

@Value for simple cases

Java
@Service
public class FeatureFlagService {
    @Value("${bookstore.features.recommendations-enabled:false}")
    private boolean recommendationsEnabled;
}

Default value after colon (:false) applies when the property is absent. Prefer @ConfigurationProperties when you have more than two related keys.

Property precedence

Spring resolves properties from multiple sources (highest priority first):

Property source precedence

  1. Command-line arguments (--server.port=9090)
  2. SPRING_APPLICATION_JSON environment variable
  3. OS environment variables
  4. application-{profile}.yml
  5. application.yml

Knowing precedence explains why a command-line override beats a YAML default.

Relaxed binding

These all bind to the same property:

Java
bookstore:
  pricing:
    service-url: https://example.com
Java
props.getServiceUrl()     // Java camelCase

Environment variable: BOOKSTORE_PRICING_SERVICE_URL.

Validation on startup

Java
@ConfigurationProperties(prefix = "bookstore.pricing")
@Validated
public record PricingProperties(
    @NotBlank String serviceUrl,
    @Positive int timeoutMs
) {}

Invalid config fails at startup with a clear BindValidationException — better than a runtime NPE in production.

Quick recall

Everything you need if you only revisit this box.

  • application.yml externalises all environment-specific BookStore settings.
  • @ConfigurationProperties binds typed, validated config objects from custom prefixes.
  • Profiles (dev, prod) switch property files and conditional beans.
  • Environment variables override YAML — use them for secrets in production.
  • Relaxed binding maps service-url, serviceUrl, and SERVICE_URL to the same field.
  • @Profile on beans registers environment-specific components like dev data seeders.

Test yourself

Answer these before moving on — recall is what makes it stick.