Lay out the property-source precedence across `@DynamicPropertySource`, `@TestPropertySource`, `@SpringBootTest(properties)`, `args`, and application config — and explain how `spring.main.*` overrides fit in.
answer
- dynamic > inlined > locations > args > sys/env > app config
- @SpringBootTest(properties) shares inlined channel with @TestPropertySource
- spring.main.* are ordinary properties, ladder applies
- webEnvironment attr supersedes web-application-type
- everything is part of the context-cache key
basics
~10 sHighest to lowest in a test: @DynamicPropertySource > @TestPropertySource/@SpringBootTest(properties) > command-line args > system properties/env > application.properties. spring.main.* keys configure SpringApplication itself and can be set through any of these, so precedence still applies.
solid answer
~40 sEffective ordering (highest wins) for a `@SpringBootTest`: (1) `@DynamicPropertySource`; (2) inlined test properties — `@TestPropertySource(properties)` and `@SpringBootTest(properties)` share this channel; (3) `@TestPropertySource(locations)` files; (4) command-line `args` option properties; (5) Java system properties then OS environment; (6) `application.properties`/`.yml` and `@PropertySource`. `spring.main.*` keys (e.g. `spring.main.web-application-type`, `banner-mode`, `lazy-initialization`, `allow-bean-definition-overriding`) aren't special property sources — they're regular properties that `SpringApplication` binds to itself early, so wherever you set them the same precedence rules decide the winner. Caveat: some settings are governed by dedicated `@SpringBootTest` attributes — `webEnvironment` effectively controls `web-application-type`, and it's cleaner to use the attribute than to fight it via `spring.main.web-application-type`. Anything read *before* binding (rare) may ignore late sources, but the standard `spring.main.*` toggles honor the ordering.
code
java · 24 lines// Demonstrates the ladder: dynamic overrides inlined overrides application.properties.
@SpringBootTest(
webEnvironment = SpringBootTest.WebEnvironment.NONE, // preferred over spring.main.web-application-type
properties = {
"spring.main.banner-mode=off",
"spring.main.lazy-initialization=true",
"app.value=from-inlined" // beats application.properties
}
)
class PrecedenceTest {
@DynamicPropertySource
static void dynamic(DynamicPropertyRegistry registry) {
registry.add("app.value", () -> "from-dynamic"); // beats the inlined value
}
@Value("${app.value}")
String value;
@Test
void dynamicWins() {
assertThat(value).isEqualTo("from-dynamic");
}
}go deeper
Know inlined test properties override application.properties.
Recite the main rungs: dynamic > inlined > args > app config.
Explain the full ladder plus how spring.main.* participate as ordinary properties.
Design a predictable, cache-efficient test-config strategy and reason about attribute-vs-property tradeoffs and startup timing.
## The full test-time precedence ladder From highest precedence (overrides everything) to lowest: 1. **`@DynamicPropertySource`** — runtime-computed suppliers; documented as higher than `@TestPropertySource`, OS env, and system properties. 2. **Inlined test properties** — `@TestPropertySource(properties = ...)` **and** `@SpringBootTest(properties = ...)` feed the *same* channel. 3. **`@TestPropertySource(locations = ...)`** — property files loaded for the test. 4. **Command-line `args`** — option arguments (`--k=v`) from `@SpringBootTest(args=...)` become the `commandLineArgs` source. 5. **`SPRING_APPLICATION_JSON`, then Java system properties, then OS environment variables** (in Boot's documented order). 6. **Config data** — `application.properties`/`application.yml`, profile-specific files, `spring.config.import`. 7. **`@PropertySource` on `@Configuration`** and **`SpringApplication` default properties** — lowest. (Devtools global settings sit even above `@TestPropertySource` in Boot's list, but they're absent in test/CI, so ignore them for interviews.) The practical takeaways: a **dynamic** value beats an **inlined** value beats an **arg** beats a **system property** beats **`application.properties`**. If a property "won't take effect," something higher on this ladder is setting it. ## Where `spring.main.*` fits `spring.main.*` are **ordinary configuration properties** bound to the `SpringApplication`/`SpringApplicationBuilder` at startup — not a separate mechanism. Common ones: - `spring.main.web-application-type` = `none` | `servlet` | `reactive` — whether a web server starts. - `spring.main.banner-mode` = `off` | `console` | `log`. - `spring.main.lazy-initialization` = `true` to defer bean creation. - `spring.main.allow-bean-definition-overriding` = `true`. - `spring.main.add-command-line-properties` = `false` to stop `args` from becoming a property source. Because they're plain properties, you can set them via `@SpringBootTest(properties = "spring.main.banner-mode=off")`, a `@TestPropertySource`, an `args` option, or `application.properties` — and the **same precedence ladder** decides which value applies. Example: `spring.main.lazy-initialization=false` in `application.properties` can be overridden by `spring.main.lazy-initialization=true` inlined on the test. ## Important nuances / gotchas - **Dedicated attributes can supersede `spring.main.*`.** `@SpringBootTest(webEnvironment = MOCK | RANDOM_PORT | DEFINED_PORT | NONE)` governs the web environment; `NONE` corresponds to no server. Prefer the attribute over hand-setting `spring.main.web-application-type` — mixing them invites confusion. - **Timing.** A handful of settings are consumed very early in `SpringApplication` bootstrap. The mainstream `spring.main.*` toggles are bound from the merged `Environment` (so precedence holds), but this is why `spring.main.*` can occasionally feel order-sensitive versus programmatic `SpringApplicationBuilder` calls — a programmatic call in `main` is code, not a property, and isn't part of this ladder. - **`add-command-line-properties=false`** removes level 4 entirely: option args stop feeding the Environment, though `ApplicationArguments` still holds them. - **Context caching.** Every one of these (properties, args, `@TestPropertySource`, `@DynamicPropertySource`, even `webEnvironment`) is part of the context-cache key. Divergent config across many test classes multiplies context startups — a real CI cost; standardize test config to maximize cache reuse. ## When this matters in design When building a shared test harness, decide a single source of truth: a base test class with `@TestPropertySource(locations="classpath:test.properties")` for stable knobs, `@DynamicPropertySource` only for container-derived values, and reserve inlined `properties` for per-test deviations. This keeps the ladder predictable and the context cache warm.
- Is `spring.main.web-application-type` the right way to make a `@SpringBootTest` non-web?It works, but the idiomatic way is `@SpringBootTest(webEnvironment = NONE)`. The attribute is clearer and avoids conflicts with Boot's own web-environment handling.
- How does setting `spring.main.add-command-line-properties=false` change the ladder?It removes command-line `args` as a property source (level 4), so option args no longer feed the Environment — though `ApplicationArguments` still exposes them for runners.
- Why should a large test suite standardize this configuration?Each distinct combination of properties/args/@TestPropertySource/@DynamicPropertySource/webEnvironment is part of the context-cache key; divergence forces extra context startups, slowing CI.
saying these in an interview costs you the question
- Placing `@TestPropertySource` above `@DynamicPropertySource`
- Treating `spring.main.*` as a special non-property mechanism
- Thinking `args` overrides `@SpringBootTest(properties)`
- Ignoring that config choices fragment the context cache