# core-events Companion project for the article **[Spring Application Events: @EventListener, @TransactionalEventListener and Async Events](https://ankurm.com/spring-application-events-eventlistener-transactionaleventlistener-async/)** on **[ankurm.com](https://ankurm.com)**. Every console block quoted in the article came out of `output/`. Transcripts 01-11 are written by the test suite (so a claim that stops being true turns the build red); 12-13 are read out of jars by `scripts/capture-facts.sh`. There is deliberately **no `docs/` folder**: the deeper material lives in collapsible "going deeper" sections inside the article itself, next to the paragraph each one extends. ## Versions | | | |---|---| | Spring Boot | 4.1.1 | | Spring Framework | 7.0.9 | | JDK | 25 (Temurin 25.0.4.1+1) | | Maven | 3.9 | | H2 | the version Boot 4.1.1 manages | | Spring Modulith (javap only) | 2.1.1 | ## Quickstart ```bash export JAVA_HOME=/path/to/jdk-25 mvn test # runs the scenarios and rewrites output/01-11 ./scripts/run-all.sh # everything, including 12-13 (needs network access to Maven Central once) ``` ## Source layout Each scenario is its own package with its own `@SpringBootApplication`, which scans only that package, so a scenario starts with exactly the beans it shows and nothing else. | Package | What it holds | |---|---| | `basics/` | `OrderService` publishes an `OrderPlaced` record; two listeners; the publisher blocks | | `ordering/` | `@Order` across two beans, an event returned from a listener, a listener that throws | | `conditional/`, `badcondition/` | SpEL `condition`s, and one that names a property that does not exist | | `generics/` | `Created` and its erasure problem, and `ResolvableTypeProvider` | | `tx/` | one listener per transaction phase, a publisher that commits or rolls back, `fallbackExecution` | | `txwrite/` | an `AFTER_COMMIT` listener that writes: nothing declared, `REQUIRES_NEW`, plain `@Transactional` | | `asyncdemo/` | `@Async` listeners, the thread they land on, and what they see of the publisher's transaction | | `asyncplain/` | the same `@Async` listener in an application that forgot `@EnableAsync` | | `support/` | `Trace` (the in-memory log the tests print), `Modes`, and `Visibility` (asks "could another connection see this row?") | ## Captured output | File | What it shows | |---|---| | `01-publish-is-a-method-call.txt` | a listener runs on the publisher's thread, before `publishEvent` returns | | `02-order-and-chaining.txt` | `@Order` across beans, an event returned from a listener, and a throwing listener stopping the rest | | `03-conditional-listeners.txt` | which SpEL-conditioned listeners ran for four events; the failure for a misspelled property | | `04-generic-events-and-erasure.txt` | a generic event that reaches no listener, and the `ResolvableTypeProvider` fix | | `05-transaction-phases-commit-and-rollback.txt` | every phase for a commit and for a rollback, with row visibility from another connection | | `06-no-transaction-drops-the-event.txt` | publishing outside a transaction, and `fallbackExecution` | | `07-after-commit-writes.txt` | an `AFTER_COMMIT` listener that inserts, declared three ways | | `08-exceptions-and-transactions.txt` | which listener's exception reaches the caller, and what it does to the row | | `09-async-listener-threads.txt` | an `@Async` listener on a platform thread, on a virtual thread, and with `@EnableAsync` forgotten | | `10-async-and-the-transaction.txt` | what an async listener can see of the publisher's transaction | | `11-async-exceptions.txt` | an async listener's exception: not the publisher's problem, but logged | | `12-modulith-application-module-listener.txt` | the annotations `@ApplicationModuleListener` is made of | | `13-transaction-cleanup-calls.txt` | the calls `DataSourceTransactionManager` makes when it cleans up |