Explain active vs default profiles and the subtle gotchas around @Profile("default") and negation.
answer
- active = explicit, default = fallback when active empty
- @Profile(default) dies the moment ANY profile active
- !prod loads with empty active set
- default is all-or-nothing
- no @Profile = always on
basics
~20 sActive profiles are the ones you explicitly turned on. Default profiles are the fallback used only when zero profiles are active (default name: 'default'). @Profile("default") beans load solely in that no-active-profile case; @Profile("!x") loads whenever x is not active.
solid answer
~40 s**Active** = explicitly enabled via `spring.profiles.active`/programmatic API; `getActiveProfiles()` returns them (empty array if none). **Default** = the fallback set (`getDefaultProfiles()`, default value `["default"]`, overridable via `spring.profiles.default`) that Spring uses in acceptance checks *only when the active set is empty*. Key gotchas: (1) `@Profile("default")` beans activate **only** when nothing else is active — activate *any* profile and they vanish, which surprises people who use them as always-on fallbacks. (2) A negation `@Profile("!prod")` bean loads even when the active set is empty, because prod isn't active. (3) Once you activate one profile, the whole default set is dropped, so several `@Profile("default")` beans disappear together. (4) In Spring Boot you can't set `spring.profiles.active` in a profile-specific document, and property-file-based defaults interplay with `application-default.properties`. Understanding this prevents mysterious missing/extra beans across environments.
code
java · 15 lines// Baseline that should ALWAYS exist — no @Profile
@Bean Clock clock() { return Clock.systemUTC(); }
// Fallback ONLY when no profile is active
@Bean @Profile("default")
DataSource h2Fallback() { return inMemory(); }
// Everywhere EXCEPT prod (loads even with empty active set)
@Bean @Profile("!prod")
DebugEndpoint debug() { return new DebugEndpoint(); }
// If you start with -Dspring.profiles.active=dev:
// clock() -> loaded (no profile)
// h2Fallback() -> NOT loaded (default set dropped once 'dev' active)
// debug() -> loaded (prod not active)go deeper
Likely only knows 'default is what runs when nothing is set'.
Can state active vs default and that default applies when none active.
Articulates the @Profile("default")-drops-when-any-active gotcha and negation-with-empty-set behavior.
Guides team conventions: unannotated for always-on, reserves 'default' for dev fallback, warns on profile sprawl, and knows Boot's activation-document and profile-group rules.
## Two distinct sets The `Environment` tracks **two** profile collections: - **Active profiles** — explicitly enabled. Sources: `spring.profiles.active`, `SPRING_PROFILES_ACTIVE`, `-D`, `setActiveProfiles`/`setAdditionalProfiles`. `getActiveProfiles()` → possibly empty `String[]`. - **Default profiles** — fallback. `getDefaultProfiles()` returns `["default"]` unless changed via `spring.profiles.default` / `setDefaultProfiles`. ### The core rule When `acceptsProfiles`/`ProfileCondition` evaluates a **positive** profile name, it checks the **active** set; if the active set is **empty**, it checks against the **default** set instead. So default profiles only ever matter when *nothing* is active. ## Gotcha 1 — @Profile("default") is not 'always on' Beans annotated `@Profile("default")` are the classic fallback (e.g., an in-memory datasource). But they load **only** while the active set is empty. The moment you set `spring.profiles.active=dev`, the default set is no longer consulted and **all** `@Profile("default")` beans drop out at once. Teams that scatter `@Profile("default")` as 'baseline' beans then activate one profile in prod and lose the baseline unexpectedly. Fix: put truly-always beans with **no** `@Profile`, and reserve `default` for genuine no-profile fallback. ## Gotcha 2 — negation and the empty active set `@Profile("!prod")` uses the active set: with nothing active, `prod` is not active, so the bean **loads**. This is often desired (debug tools everywhere but prod) but people wrongly assume a fallback bean should be `@Profile("!prod")` when they actually mean 'only when no profile' (`default`). They are different predicates. ## Gotcha 3 — default is all-or-nothing Activating any single profile discards the *entire* default set, not just the overlapping names. Multiple `@Profile("default")` beans vanish together. ## Gotcha 4 — Spring Boot specifics - `spring.profiles.active` **must not** be set inside a profile-specific document/file (Boot 2.4+); use `spring.profiles.group`/`spring.config.activate.on-profile` instead. - `spring.profiles.default` changes the fallback name; `application-<default>.properties` follows it. - `spring.profiles.include` (older) / `spring.profiles.group` compose profiles. ## Diagnosing - Log `env.getActiveProfiles()` and `env.getDefaultProfiles()` at startup (Boot logs 'The following profiles are active' / 'No active profile set, falling back to default profiles'). - Missing bean in prod? Check whether it was silently a `@Profile("default")`. ## When to use which - **No @Profile**: infrastructure that must exist everywhere. - **@Profile("default")**: dev-time convenience fallback that should disappear once any real environment is selected. - **@Profile("!prod")**: cross-environment-except-prod behavior. - **@Profile("prod")**: environment-specific real implementations. ## Edge: combining `@Profile("default | test")` matches when nothing is active OR test is active — legal but check it's the intent, since the default half only fires with an empty active set.
- You set spring.profiles.active=dev and your @Profile("default") datasource disappears. Why?Default profiles are only consulted when the active set is empty. Activating 'dev' makes the active set non-empty, so the default set is never checked and @Profile("default") beans are excluded.
- What's the difference between @Profile("default") and @Profile("!prod") for a fallback bean?"default" loads only when NO profile is active. "!prod" loads whenever prod is not among the active profiles — including dev, test, or an empty active set. They coincide only when nothing is active.
- How do you make a bean truly always present regardless of profiles?Don't annotate it with @Profile at all. Unannotated beans are registered in every configuration regardless of active/default profiles.
saying these in an interview costs you the question
- Treating @Profile("default") as an always-on baseline
- Thinking default profiles are merged with active ones
- Assuming activating one profile only drops the same-named default
- Conflating @Profile("!prod") with @Profile("default")
- Setting spring.profiles.active inside a profile-specific Boot document