Companion module for the rewritten post 'Resilience4j Circuit Breaker in Spring Boot 4.1: What It's Still For', reworked around Framework 7 now shipping @Retryable/@ConcurrencyLimit in core. Covers what's still Resilience4j's job (circuit breaker, rate limiter, bulkhead's bounded wait, fallback methods, Actuator/Micrometer metrics), the off-by-one between maxAttempts and maxRetries, and two Boot-4.1 build breaks: spring-boot-starter-aop no longer exists (renamed to spring-boot-starter-aspectj, proven with Maven Central metadata and the renamed starter's own POM -- see resilience/docs/08-starter-aop-renamed-to-starter-aspectj.md in this same repo), and the resulting fix uses that renamed starter directly rather than assembling spring-aop + aspectjweaver by hand. Kept as its own module rather than a new top-level repository, alongside the existing resilience/ module for the sibling Framework-7 post.
resilience4j-circuit-breaker
Companion module for the ankurm.com post "Resilience4j Circuit Breaker in Spring Boot 4.1:
What It's Still For". Every
claim in the post about the new @Retryable/@ConcurrencyLimit API in Spring Framework 7, and
about how it does and doesn't overlap with Resilience4j, is backed by a file under
docs/output/ produced by a real test run against real Maven Central artifacts.
Lives in this container repo (not as its own top-level repository) alongside
../resilience, the companion module for the sibling post on Framework 7's
built-in @Retryable/@ConcurrencyLimit. The two modules overlap in theme by design — this one
is entered from "I want a circuit breaker," that one from "what did Framework 7 just ship" — and
intentionally keep separate demo apps rather than sharing one, since each post's transcripts need
to stay independently reproducible from its own module.
Versions (verified against repo1.maven.org maven-metadata.xml, not aggregators)
| Component | Version | Notes |
|---|---|---|
| JDK | 25 (Temurin 25.0.4.1+1) | latest LTS |
| Spring Boot | 4.1.1 | latest GA at time of writing; 4.2.0-M1 exists but is a milestone |
| Spring Framework | 7.0.9 | latest GA; 7.1.0-M1 exists but is a milestone |
| Resilience4j | 2.4.0 (resilience4j-spring-boot4) |
depends internally on resilience4j-spring6, not a "spring7" module — see docs/01 |
Quickstart
./scripts/run-all.sh # regenerates every file in docs/output/ from a real test run
./scripts/run.sh # starts the app on :8080 to poke at by hand
curl localhost:8080/actuator/health
curl localhost:8080/actuator/circuitbreakers
Requires JDK 25 and Maven. First run must be online (Maven needs to fetch plugins into the
local cache); -o works for subsequent builds.
What's demonstrated where
| Area | Source | Test | Transcript |
|---|---|---|---|
| Resilience4j circuit breaker: trip, stay open, half-open, recover | R4jPaymentService |
CircuitBreakerTripAndRecoverTest |
01 |
@Retryable: recovers from transient failure |
SpringRetryablePaymentService |
SpringRetryableTest |
03a |
@Retryable: no memory between calls |
same | same | 03b |
@Retryable: self-invocation trap |
same | same | 03c |
Resilience4j @Retry side-by-side (maxAttempts counts differently than maxRetries) |
R4jRetryService |
R4jRetryTest |
03d, 03e |
@ConcurrencyLimit BLOCK policy |
ConcurrencyLimitedService |
ConcurrencyLimitTest |
04a |
@ConcurrencyLimit REJECT policy |
same | same | 04b |
Resilience4j @Bulkhead (bounded wait) for comparison |
R4jBulkheadService |
same | 04c |
Real /actuator/health + /actuator/circuitbreakers with a breaker OPEN |
R4jPaymentService |
ActuatorHealthTest |
05 |
Endpoints (from scripts/run.sh)
| Endpoint | Purpose |
|---|---|
GET /actuator/health |
includes circuit breaker health when management.health.circuitbreakers.enabled=true |
GET /actuator/circuitbreakers |
live circuit breaker state |
GET /actuator/circuitbreakerevents |
event stream of state transitions |
GET /actuator/metrics |
includes resilience4j.circuitbreaker.* Micrometer series |
There is no custom diagnostic endpoint in this repo — the standard Actuator set above already exposes everything the post needed, so nothing has to be deleted before shipping.
Note on /actuator/health in 2.4.0: with management.endpoint.health.show-details: always,
/actuator/health reports only an aggregate "circuitBreakers":{"status":"UNKNOWN"} — no
per-instance breakdown. The per-breaker detail (state, failureRate, bufferedCalls, etc.)
lives at /actuator/circuitbreakers instead. Real captured output of both, side by side, with
the breaker actually OPEN: docs/output/05-actuator-health-tripped.txt.
Documentation chapters
- Two resilience stacks on one classpath — what moved into Spring Framework 7 core, what didn't, and two build breaks you'll hit getting there
- The circuit breaker, verified
- @Retryable: retries, but no memory
- @ConcurrencyLimit vs Resilience4j's Bulkhead
- Production checklist: which one, for what
License
MIT — see LICENSE.