Q: How do Spring profiles work? How do you handle environment-specific config?
Answer:
Profiles = named groups of beans + properties. Activate per-environment (dev/staging/prod) without code changes.
Activate Profiles
Multiple ways, increasing priority:
# property
spring.profiles.active=prod
# env var
export SPRING_PROFILES_ACTIVE=prod
# CLI
java -jar app.jar --spring.profiles.active=prod
# JVM
-Dspring.profiles.active=prod
Multiple: dev,debug,local.
Profile-Specific Properties
Boot automatically loads:
application.yml # base — always loaded
application-dev.yml # only when "dev" active
application-prod.yml # only when "prod" active
Profile properties override base.
Profile-Specific Beans
@Configuration
@Profile("prod")
public class ProdMailConfig {
@Bean MailSender mailSender() { return new SesMailSender(); }
}
@Configuration
@Profile("!prod") // any non-prod
public class DevMailConfig {
@Bean MailSender mailSender() { return new ConsoleMailSender(); }
}
Method-level too:
@Bean @Profile("prod") DataSource prodDs() { ... }
@Bean @Profile({"dev","test"}) DataSource devDs() { ... }
YAML Multi-Document
Single file, multiple profiles (Boot 2.4+):
spring:
application.name: my-app
---
spring:
config.activate.on-profile: dev
server:
port: 8080
---
spring:
config.activate.on-profile: prod
server:
port: 80
Profile Groups (Boot 2.4+)
spring:
profiles:
group:
production: prod, monitoring, audit
Activate production → all three flip on.
Default Profile
If none active, default profile is. application-default.yml loads. Override:
spring.profiles.default=local
Conditional Beans Beyond Profiles
For finer control:
@ConditionalOnProperty(name = "feature.payments.v2", havingValue = "true")
@Bean PaymentClient v2Client() { ... }
Programmatic Activation
SpringApplication app = new SpringApplication(App.class);
app.setAdditionalProfiles("prod");
app.run(args);
Tests
@SpringBootTest
@ActiveProfiles({"test", "h2"})
class OrderServiceTest { ... }
Common Patterns
1. External secrets per env
# application.yml
db:
url: ${DB_URL}
password: ${DB_PASSWORD}
Profile decides which env vars are set in deployment manifest.
2. Mock vs real integrations in dev
@Profile("local") @Service class FakePaymentClient implements PaymentClient {...}
@Profile("!local") @Service class StripePaymentClient implements PaymentClient {...}
3. Cloud config + profiles
Spring Cloud Config server can serve profile-specific files (app-prod.yml) from git.
Pitfalls
- Forgot to activate → bean missing →
NoSuchBeanDefinitionException. - Multiple profile files but typo in profile name → silently uses defaults.
- Tests inheriting prod profile → hitting real services. Always set
@ActiveProfiles("test"). - Property precedence: command-line > env vars > application-{profile}.yml > application.yml. Knowing this matters when debugging "why is this value not what I set".
Profile-Aware Property Sources Order (highest precedence first)
- Command-line args
SPRING_APPLICATION_JSONapplication-{profile}.properties/yml(external)application.properties/yml(external)application-{profile}.properties/yml(classpath)application.properties/yml(classpath)@PropertySource- Default properties
Profile-specific always wins over base at the same level.