skip to content

How does Spring decide the order in which the default listeners (DependencyInjection, DirtiesContext, Transactional, Sql) run, and why does that order matter?

level: seniorimportance: should knowfreq 25%

answer

  1. Ordered/@Order sorts listeners; before* ascending, after* reverse
  2. DI 2000 < DirtiesCtx 3000 < Tx 4000 < Sql 5000
  3. Tx before Sql so @Sql runs inside the rolled-back tx
  4. custom listener: order >4000 to be inside the tx
  5. same order value => undefined sequence

basics

~20 s

Each listener has an order value (via Ordered/@Order); Spring sorts by it. Lower value runs earlier in before* callbacks. Roughly: DependencyInjection (2000) < DirtiesContext (3000) < Transactional (4000) < Sql (5000). Order matters so injection and transactions are set up before SQL runs.

solid answer

~40 s

Default listeners are registered from spring.factories and each declares a precedence via the Ordered interface. Spring sorts them ascending, and for before* callbacks lower order runs first (after* callbacks run in reverse). The core ordering is DependencyInjectionTestExecutionListener (~2000), then DirtiesContextTestExecutionListener (~3000), then TransactionalTestExecutionListener (~4000), then SqlScriptsTestExecutionListener (~5000), with EventPublishingTestExecutionListener last (~10000). This ordering is deliberate: the test must be injected before anything uses it; the transaction must be started (Transactional in beforeTestMethod) before @Sql scripts run so that BEFORE_TEST_METHOD scripts execute inside the test's transaction and get rolled back with it. When you merge a custom listener you pick its @Order value to slot it into the right place relative to these.

go deeper

for a junior

Know that order is controlled by @Order/Ordered.

for a middle

Recall DI < Transactional < Sql and that before* is ascending.

for a senior

Explain why Tx-before-Sql matters and place a custom listener correctly.

for a principal

Reason about reverse after* ordering, dirty-context-before-inject, and order collisions.

## Ordering mechanism Every `TestExecutionListener` may implement `org.springframework.core.Ordered` (or carry `@Order`). The `TestContextManager` sorts the registered listeners by their order value using `AnnotationAwareOrderComparator`. Semantics: - **before* callbacks** (`beforeTestClass`, `prepareTestInstance`, `beforeTestMethod`, `beforeTestExecution`): iterated in ascending order — **lower value = earlier**. - **after* callbacks**: iterated in **reverse** — the listener that set something up first tears it down last (nested-resource discipline). ## The default order values (Spring's own listeners) Approximate, stable across recent Spring versions: | Listener | Order | |---|---| | ServletTestExecutionListener | 1000 | | DirtiesContextBeforeModesTestExecutionListener | 1500 | | ApplicationEventsTestExecutionListener | 1800 | | DependencyInjectionTestExecutionListener | 2000 | | MicrometerObservationRegistryTestExecutionListener | 2500 | | DirtiesContextTestExecutionListener | 3000 | | TransactionalTestExecutionListener | 4000 | | SqlScriptsTestExecutionListener | 5000 | | EventPublishingTestExecutionListener | 10000 | (Exact numbers can shift; the *relative* order is the point and is what you reason about.) ## Why the order is deliberate 1. **DependencyInjection before everything else that touches the instance** — the test object must be autowired before a transaction or SQL logic could rely on its injected fields. 2. **Transactional (4000) before Sql (5000)** — this is the critical one. `TransactionalTestExecutionListener` opens the transaction in `beforeTestMethod`; because it runs *before* `SqlScriptsTestExecutionListener`, a `@Sql` script with the default `executionPhase = BEFORE_TEST_METHOD` runs **inside** that transaction (unless the `@SqlConfig` transactionMode says otherwise) and is rolled back together with the test. If Sql ran before Transactional, setup data could be committed outside the test's transaction. 3. **DirtiesContext BEFORE modes early (1500)** — must close/rebuild the context *before* injection happens, otherwise injection would use a stale context. 4. **Event publishing last** — it broadcasts lifecycle events after the functional listeners have done their work. ## Practical consequence for custom listeners When you add your own listener with `MERGE_WITH_DEFAULTS`, choose `@Order` relative to this table. Need to seed data inside the test's transaction? Give your listener an order **> 4000** (after Transactional) so the tx is already open. Need to run before injection? Order **< 2000**. ## Gotcha Two listeners with the same order value have undefined relative ordering — don't collide with a default's exact value if sequence matters.

  • Why must TransactionalTestExecutionListener run before SqlScriptsTestExecutionListener?
    So a BEFORE_TEST_METHOD @Sql script executes inside the test's transaction and is rolled back with it, rather than committing setup data outside the test.
  • If you want a custom listener to seed data inside the test transaction, what order should it have?
    Greater than TransactionalTestExecutionListener's (~4000) so the transaction is already started when your listener runs.

saying these in an interview costs you the question

  • Thinking listeners run in declaration/registration order regardless of @Order
  • Assuming after* callbacks run in the same order as before*
  • Believing @Sql always commits independently of the test transaction

context