How can you activate or change profiles programmatically, and why is timing critical?
answer
- ConfigurableEnvironment = mutable Environment
- setActiveProfiles replaces / addActiveProfiles appends
- must run before refresh()
- @Profile evaluated during registration
- no live profile hot-swap
basics
~10 sUse ConfigurableEnvironment (from context.getEnvironment()): setActiveProfiles(...) or addActiveProfiles(...). This must happen before the context is refreshed, because @Profile is evaluated during bean registration. After refresh it's too late.
solid answer
~40 s`Environment` is read-only; `ConfigurableEnvironment` adds mutators: `setActiveProfiles(String...)` (replaces the set), `addActiveProfiles(String)` (appends one), and `setDefaultProfiles(...)`. You reach it before startup, e.g. `new SpringApplicationBuilder(App.class).profiles("prod").run()`, `SpringApplication.setAdditionalProfiles(...)`, or on a raw context: `ctx.getEnvironment().setActiveProfiles("prod")` then `ctx.refresh()`. **Timing is the whole story**: `@Profile` is a `@Conditional` evaluated while bean definitions are being registered, which happens *during* `refresh()`. If you mutate profiles after the context is refreshed, existing beans are already decided and nothing re-registers — the change is effectively inert for `@Profile`. So programmatic activation must occur pre-refresh, or via `spring.profiles.active`/env before the context builds. There is no supported way to 'reload' a running context's beans by flipping a profile.
code
java · 10 linespublic static void main(String[] args) {
var ctx = new AnnotationConfigApplicationContext();
ConfigurableEnvironment env = ctx.getEnvironment();
env.setActiveProfiles("prod", "cloud"); // BEFORE refresh — honored
ctx.register(AppConfig.class);
ctx.refresh(); // @Profile evaluated here
env.addActiveProfiles("debug"); // AFTER refresh — inert for @Profile
// getActiveProfiles() now shows debug, but no new beans registered
}go deeper
May only know the property-based activation, not programmatic.
Know ConfigurableEnvironment.setActiveProfiles exists and is set before startup.
Explain the pre-refresh timing constraint tied to @Conditional evaluation, and replace-vs-append semantics.
Discuss initializer/EnvironmentPostProcessor hooks, why profiles are a startup concern not a runtime toggle, and steering teams toward feature flags for runtime behavior changes.
## Environment vs ConfigurableEnvironment `org.springframework.core.env.Environment` exposes reads. Its subinterface **`ConfigurableEnvironment`** adds mutation: - `void setActiveProfiles(String... profiles)` — **replace** the active set. - `void addActiveProfiles(String profile)` — **append** one profile. - `void setDefaultProfiles(String... profiles)` — change the fallback set. - `MutablePropertySources getPropertySources()` — add/remove/reorder property sources. - `getSystemProperties()`, `getSystemEnvironment()`, `merge(...)`. Every `ApplicationContext` returns a `ConfigurableEnvironment` from `getEnvironment()` (the return type on `ConfigurableApplicationContext` is already `ConfigurableEnvironment`). ## Ways to activate profiles programmatically 1. **Raw context (rare, non-Boot):** ```java var ctx = new AnnotationConfigApplicationContext(); ctx.getEnvironment().setActiveProfiles("prod", "cloud"); ctx.register(AppConfig.class); ctx.refresh(); // registration + @Profile evaluation happens here ``` 2. **Spring Boot builder:** `new SpringApplicationBuilder(App.class).profiles("prod").run(args);` 3. **SpringApplication:** `app.setAdditionalProfiles("prod");` before `run` — *adds* without erasing config-file profiles. 4. **ApplicationContextInitializer / EnvironmentPostProcessor** — mutate the environment very early, before refresh. 5. Externally (preferred): `spring.profiles.active`, `SPRING_PROFILES_ACTIVE`, `-Dspring.profiles.active=...`. ## Why timing is critical `@Profile` is meta-annotated `@Conditional(ProfileCondition.class)`. Conditions are evaluated by the `ConfigurationClassPostProcessor` / bean-definition registration phase, which runs **inside `AbstractApplicationContext.refresh()`**. Consequences: - Mutating `setActiveProfiles` **before** `refresh()` → honored. - Mutating **after** `refresh()` → the profile set changes in the `Environment`, and `getActiveProfiles()` will report it, but **no beans are re-registered or removed**. `@Profile` gating already happened. So it's effectively a no-op for wiring. - There is no built-in 'hot swap' of profiles for a live context. To change profiles you rebuild/restart the context. ## setActiveProfiles vs addActiveProfiles vs setAdditionalProfiles - `setActiveProfiles` **overwrites** — dangerous if you wanted to keep file-configured profiles. - `addActiveProfiles`/Boot's `setAdditionalProfiles` **augment** the existing set. ## Interaction with spring.profiles.active If both a property and programmatic call set profiles, the last write before refresh wins for that environment; Boot's `setAdditionalProfiles` merges rather than overrides the property-driven ones. ## Gotchas - Calling `setActiveProfiles` inside a `@Bean` method or a running bean is too late. - Empty varargs `setActiveProfiles()` clears active profiles (reverting to defaults). - Property sources added via `getPropertySources()` after refresh will be seen by later `getProperty` calls but won't retroactively affect already-resolved `@Value` injections.
- You call setActiveProfiles after refresh() — will a @Profile("debug") bean appear?No. @Profile is evaluated during context refresh/bean registration. After refresh the beans are already decided; the environment reports the new profile but no bean definitions are re-processed.
- Difference between SpringApplication.setAdditionalProfiles and Environment.setActiveProfiles?setAdditionalProfiles augments the profiles resolved from configuration (merges), while setActiveProfiles on the environment overwrites the active set entirely. Additional is safer when you want to keep file/env-driven profiles.
- How would you actually switch environments at runtime?You don't flip a profile on a live context. You restart with a different spring.profiles.active, or design runtime-configurable behavior with regular beans/feature flags rather than @Profile.
saying these in an interview costs you the question
- Thinking setActiveProfiles after refresh re-wires beans
- Believing you can hot-swap profiles on a running context
- Confusing addActiveProfiles (append) with setActiveProfiles (replace)
- Assuming @Profile is evaluated lazily at bean access time