skip to content

Explain the dual behavior of @DisabledInAotMode (build-time vs runtime) and how Spring detects AOT mode.

level: seniorimportance: should knowfreq 35%

answer

  1. build-time: generator skips the class
  2. runtime: JUnit condition disables the test
  3. spring.aot.enabled=true
  4. AotDetector.useGeneratedArtifacts()
  5. TestContextAotGenerator / process-test-aot

basics

~20 s

It acts at two moments: during build-time test AOT processing it tells Spring to skip generating optimized artifacts for that context, and at runtime it disables the test when AOT mode is active. AOT mode is detected via the spring.aot.enabled system property.

solid answer

~40 s

@DisabledInAotMode has a build-time and a runtime effect. At build time, Spring's test AOT generation (the process-test-aot step) walks the unique test ApplicationContexts to emit optimized, reflection-free code; classes marked @DisabledInAotMode are excluded so the generator doesn't attempt (and fail) to refresh a context that mutates itself. At runtime, when the app runs against generated AOT artifacts, Spring reports being in AOT mode via the system property spring.aot.enabled=true, which Spring reads through org.springframework.aot.AotDetector.useGeneratedArtifacts(). A Spring-provided JUnit Jupiter execution condition consults that flag and disables the annotated test, so it's skipped rather than executed against a frozen context. The net effect: the test participates fully in normal JVM runs, is omitted from AOT artifact generation, and is skipped when executed in AOT mode.

go deeper

for a junior

Know there is a build-time and a runtime aspect, even if not the internals.

for a middle

Name the runtime detection via spring.aot.enabled and that build-time generation skips the class.

for a senior

Explain both effects precisely and cite AotDetector.useGeneratedArtifacts() and the JUnit execution condition.

for a principal

Discuss why both effects are jointly necessary and how the flag ties into Spring's broader generated-artifact consumption.

## The two moments ### 1. Build-time: test AOT processing When you build a native (or AOT-optimized) **test** image, a `process-test-aot` phase runs. Spring's test AOT infrastructure (`TestContextAotGenerator`) discovers every **unique** test `ApplicationContext` configuration, **refreshes each one at build time**, and generates Java source that recreates it without runtime reflection/scanning. If a test's context mutates itself (mocks, dynamic bean registration), refreshing it during AOT generation typically **fails**. `@DisabledInAotMode` makes the generator **skip** that class, so it never tries to produce AOT artifacts for that context. ### 2. Runtime: execution in AOT mode When the application starts against generated artifacts, Spring is in **AOT mode**. This is signaled by the JVM system property **`spring.aot.enabled=true`** and queried in code via `org.springframework.aot.AotDetector.useGeneratedArtifacts()`. A **Spring-provided JUnit Jupiter `ExecutionCondition`** checks the annotation together with that flag and **disables** the test (JUnit reports it skipped) so it never runs against the frozen context. ## How AOT mode is detected — details - `AotDetector.useGeneratedArtifacts()` returns `true` when `spring.aot.enabled` is set to `true`. - You do not usually set this by hand for tests; the native/AOT test tooling sets it. But you *can* set `-Dspring.aot.enabled=true` to simulate AOT mode on the JVM against previously generated artifacts. - The same detector is what Spring's own framework code uses to decide whether to consume generated bean-registration code versus doing runtime processing. ## Why both effects are needed - Build-time skip alone would still let the test try to run in AOT mode (and misbehave). - Runtime disable alone would still let the build-time generator choke on the context. Together they make the test a clean no-op across the whole AOT pipeline while remaining a first-class test on the JVM. ## Gotchas / edge cases - Because it's conditional on AOT mode, code coverage and CI gates that only run plain JVM tests won't notice anything — the difference only shows in the native/AOT job. - Don't confuse it with JUnit's unconditional `@Disabled` or with `@DisabledIfSystemProperty` — `@DisabledInAotMode` is specifically wired to Spring's AOT detection and to the AOT generator. - It applies per unique context: two test classes sharing one context config are evaluated independently for annotation presence.

  • How can you make a plain JVM run behave as if in AOT mode to exercise the disable path?
    Set the system property -Dspring.aot.enabled=true (against generated artifacts). AotDetector.useGeneratedArtifacts() then returns true and the annotated tests are skipped.
  • How does @DisabledInAotMode differ from JUnit's @Disabled?
    @Disabled unconditionally skips a test everywhere. @DisabledInAotMode only skips when Spring is in AOT mode and also excludes the class from build-time AOT artifact generation; on a normal JVM run the test executes.

saying these in an interview costs you the question

  • Saying it disables the test in every environment like @Disabled
  • Claiming AOT mode is detected by checking for GraalVM at runtime rather than the spring.aot.enabled property
  • Believing it only affects build time and does nothing at runtime

context