Spring Boot profiles help you manage environment-specific behavior, but one of their most powerful features is profile-based bean loading — selectively creating beans based on which profile(s) are active.
This guide explains:
- What profile-based beans are
- How Spring Boot decides which beans to create
- How to write beans per environment
- Real world usage patterns
- Common pitfalls and best practices
Whether you’re running local development, test automation, staging, or production clusters, controlled bean loading is a cornerstone of high-quality Spring Boot architecture.
What Is Profile-Based Bean Loading?
Profile-based bean loading means Spring Boot will only instantiate certain beans if specific profiles are active.
If a bean is annotated with a profile that doesn’t match the active profiles:
- Spring will not create the bean
- Dependencies won’t be injected
- The application context will not contain that bean
This allows you to define beans for:
✔ Development
✔ Test
✔ Production
✔ Feature flags
✔ Cloud-specific behavior
✔ Experimental modes
How Spring Boot Decides Which Beans to Load
When Spring Boot starts up:
- It determines the active profiles
- Loads all standard configuration
- Registers beans across your application
- Only instantiates beans with matching profiles
Example:
- Active profile:
dev - Bean annotated with
@Profile("dev")→ created - Bean annotated with
@Profile("prod")→ ignored
Spring does this during the bean registration phase — before dependency injection completes.
For more details on bean lifecycle and registration, see:
https://springbootfixes.com/how-spring-boot-creates-beans-bean-lifecycle-simplified-for-production/
Using @Profile on Beans
The most straightforward way to use profile-based loading is:
@Profile("dev")
@Service
public class MockEmailService implements EmailService {}
@Profile("prod")
@Service
public class SmtpEmailService implements EmailService {}
In this example:
✔ dev profile loads a mock service
✔ prod profile loads the real service
Spring will automatically create the appropriate implementation based on the active profile.
Using @Profile on Configuration Classes
You can group multiple bean definitions into one configuration class:
@Configuration
@Profile("test")
public class TestDataConfig {
@Bean
public DataSource testDataSource() {
return new EmbeddedDatabaseBuilder().build();
}
}
This is helpful when you want a dedicated configuration set per profile.
This pattern keeps production configs clean and avoids mixing environment concerns.
Logical Profile Conditions
Spring Boot supports composite profile conditions:
Multiple profiles (OR)
@Profile({"dev", "test"})
This bean loads if either profile is active.
Not profile
@Profile("!prod")
This bean will load in all profiles except prod.
This is useful when:
- Some beans should run everywhere except production
- Feature toggles apply only to specific environments
Combining Profiles with Configuration Files
In many builds, you’ll also use profile-specific configuration files:
Example:
application-dev.ymlapplication-test.ymlapplication-prod.yml
Spring Boot merges these with the base application.yml, allowing both property and bean overrides via profiles.
Setting an active profile:
SPRING_PROFILES_ACTIVE=prod
This causes Spring Boot to load application-prod.yml alongside beans with matching profiles.
Learn more about how Spring Boot loads config files here:
https://springbootfixes.com/spring-boot-configuration-loading-order-production-guide/
Real World Use Cases
Mock versus Real Services
In dev and test:
@Profile("dev")
@Bean
public PaymentGateway mockPaymentGateway() {
return new MockPaymentGateway();
}
In prod:
@Profile("prod")
@Bean
public PaymentGateway stripePaymentGateway() {
return new StripePaymentGateway();
}
This pattern helps you:
✔ Avoid real external calls during dev
✔ Keep tests fast and reliable
✔ Run optimized services in prod
Environment-Specific Integrations
Example: Metrics collectors, caching, security providers, feature toggles, etc.
Many production systems will enable additional services only in prod:
@Profile("prod")
@Bean
public MonitoringService monitoringService() {
return new PrometheusMonitoringService();
}
Common Mistakes with Profile-Based Beans
Mistake 1 — Missing Profiles
If a bean is strictly profile-based and no matching profile is active, Spring fails with a missing dependency.
Solution:
✔ Provide defaults
✔ Use @Profile("default")
Mistake 2 — Too Many Profiles
Overcomplicating profiles (dev, test, prod, staging, local, ci, cloud…) can make maintenance hard.
Tip:
✔ Keep profiles meaningful
✔ Map to environments clearly
Mistake 3 — Profile Collision
Activate conflicting profiles (e.g., dev,prod) and behavior becomes unpredictable.
Tip:
✔ Avoid mixing incompatible profiles
Best Practices in Production
✔ Use constructor injection (clear dependencies)
✔ Use @Profile sparingly (only when necessary)
✔ Prefer configuration over code changes
✔ Store secrets via env vars or vaults
✔ Log active profiles at startup
Profiles work hand-in-hand with configuration files, CI/CD pipelines, and container environments — especially in cloud deployments.
How This Fits Into Your Spring Boot Mental Model
Profile-based bean loading follows dependency injection and configuration loading:
- Startup sequence loads profiles first
- Configuration files are merged
- Bean definitions are registered
- Beans with matching profiles are instantiated
- Remaining beans behave normally
Learn more about related core concepts here:
- Spring Boot Configuration and Profiles Explained
https://springbootfixes.com/spring-boot-configuration-and-profiles-explained-beginner-to-production-guide/ - How Spring Boot Application Starts (Startup Flow)
https://springbootfixes.com/how-spring-boot-application-starts-startup-flow-explained/ - Dependency Injection in Spring Boot
https://springbootfixes.com/dependency-injection-in-spring-boot-production-grade-explanation/
Frequently Asked Questions
Can I define multiple @Profile annotations on one class?
Yes, using logical OR conditions in a single annotation.
Example:
@Profile({"dev", "staging"})
What happens when no profile is active?
Spring loads only beans without profile annotations unless you’ve explicitly defined a default profile.
How does @Profile work with Spring Security?
Profile-based beans interact normally — you may use profiles to load beans specific to security configurations per environment.
Summary
Profile-based bean loading gives you fine-grained control over what gets instantiated in each environment — making your Spring Boot application more predictable, testable, and production-ready.
With a solid profile strategy:
✔ You avoid environment configuration leaks
✔ Your application behaves consistently
✔ You can swap beans safely at runtime
