Co-Authored-By: Claude Sonnet 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01B38FGKKam5SCGgwgduVAh3
spring-boot-demo
Companion code for the Spring Boot articles on ankurm.com.
One directory per article. Each is a self-contained Maven project with its own README.md,
numbered documentation chapters under docs/, and captured real output under docs/output/
that a single script regenerates. Every figure quoted in an article came out of one of those
files.
| Directory | Article | What it demonstrates |
|---|---|---|
actuator-in-production/ |
Spring Boot Actuator in Production | endpoint exposure defaults, securing Actuator, custom health indicators and how they hang |
spring-boot-startup-time/ |
Why Your Spring Boot App Takes 8 Seconds to Start | BufferingApplicationStartup, JFR startup events, self time vs total time, the classpath-scan tax, the JDK 25 AOT cache |
configuration-properties/ |
@ConfigurationProperties vs @Value in Spring Boot 4 | relaxed binding measured three ways, record constructor binding, validation, IDE metadata generation |
profiles-and-config/ |
Spring Boot Profiles Done Right | the precedence stack made visible, config trees and ConfigMaps, why a profile file loses to an environment variable |
spring-aop/ |
Spring AOP Explained | every pointcut designator with real matches, JDK vs CGLIB proxies, six aspects that do not fire |
docker-images/ |
Dockerizing Spring Boot 4: Layered Jars, Buildpacks, Distroless and Image Size Benchmarks | one service packaged nine ways and measured: size on disk and pushed, rebuild delta, startup, PID 1 and signals, jlink, the JDK 25 AOT cache |
kubernetes-deployment/ |
Deploying Spring Boot 4 on Kubernetes | probe groups under a dependency outage, graceful shutdown under load four ways, JVM ergonomics per pod shape, CPU limits throttling GC, HPA on a Micrometer metric |
problem-details/ |
Global Exception Handling with ProblemDetail (RFC 9457) in Spring Boot 4 | thirteen failures under five handling setups, validation errors, i18n, content negotiation, errors outside MVC, silent 500s, decoding on the client |
resilience/ |
Spring Framework 7's Built-in Resilience: @Retryable, @ConcurrencyLimit, and What's Left for Resilience4j | @Retryable and @ConcurrencyLimit counted invocation by invocation, retries inside transactions, where Resilience4j still earns its place, migrating from Spring Retry |
resilience4j-circuit-breaker/ |
Resilience4j Circuit Breaker in Spring Boot 4.1: What It's Still For | the circuit breaker verified through trip/open/half-open/recover, spring-boot-starter-aop renamed to spring-boot-starter-aspectj (not removed), and where Resilience4j still beats Framework 7's own @Retryable/@ConcurrencyLimit |
observability/ |
A Practical Guide to Monitoring Spring Boot Microservices: Prometheus, Grafana, and Boot 4.1's OpenTelemetry Starter | real metrics and traces pushed via OTLP to a real grafana/otel-lgtm container, Docker Compose auto-wiring the endpoint with zero management.otlp.* properties, @Observed silently inert without an explicit ObservedAspect bean, and a dual-version (Boot 4.0 vs 4.1) proof of which OTEL_* environment variables are genuinely new |
caching/ |
The Spring Cache Abstraction: @Cacheable, @CacheEvict, Key Generators and the Self-Invocation Trap | the self-invocation trap measured four ways, the key collision SimpleKeyGenerator makes easy, eviction timing under a thrown exception, a rollback the cache keeps, and where this sits next to Hibernate's L2 cache |
redis/ |
Redis with Spring Boot 4.1: RedisTemplate, StringRedisTemplate, @RedisHash, Pub/Sub and TTL | the default JDK-serialised RedisTemplate writing keys redis-cli cannot read, before and after Jackson 3 serializers (class names read with javap, the @class allow-list), the six keys two @RedisHash entities create, expired hashes leaving index entries behind unless keyspace events are on, @RedisListener on Boot 4.1's auto-configured container, a plain set clearing a TTL, and a RedisCacheConfiguration bean silently overriding spring.cache.redis.time-to-live |
spring-batch/ |
Spring Batch on Boot 4.1: Jobs, Steps, Chunk Processing and Restartability | a job that fails mid-chunk and resumes exactly where it left off across two separate JVMs, skip vs. restart on the same poisoned row, the resourceless job repository that forgets a restart ever happened, and the chunk(int) vs chunk(int, tx) builder split |
spring-batch-partitioning/ |
Spring Batch Partitioning and Parallel Steps: Scaling a 10-Million-Row Job | the real grid-size sweep at 10M and 300K rows (best speedup 1.42x, on 2 cores), MultiResourcePartitioner ignoring gridSize entirely, a rejected partition's StepExecution stuck at STARTING forever, and Spring Batch 6.0's new JobOperator#recover unsticking it |
db-migrations-flyway-liquibase/ |
Flyway vs Liquibase for Spring Boot 4: Migrations, Rollbacks and Baselines | Flyway Community's undo throwing FlywayRedgateEditionRequiredException at runtime, a real Liquibase 5.0.3 filename-caching defect that produces a phantom successful run, Liquibase's 10-second default lock-poll rate versus Flyway's near-instant row lock, the FSL license change and its ASF/Keycloak fallout, and what actually happens when both tools are enabled against one database |
db-migrations-expand-contract/ |
Zero-Downtime Database Migrations: Expand-Contract in Practice with Spring Boot | a real 4-deploy rolling sequence against two live replicas with a load generator proving 99.98% success, H2's AUTO_SERVER single-point-of-failure trap, a NOT NULL constraint that fails every Stage 4 insert, and ALTER TABLE silently dropping a concurrently committed row with no exception thrown |
openapi-versioning/ |
springdoc-openapi with Spring Boot 4.1: Generating, Customising and Versioning Your API Spec | springdoc 3.1.1 silently merging same-path, different-version handlers into one oneOf operation with an arbitrary operationId, a working per-version fix with GroupedOpenApi + OpenApiCustomizer, and the officially-versioning-supported functional-endpoint path turning out to document only one of two registered versions |
graphql-dataloader/ |
Spring GraphQL 2.0: Schema-First APIs, DataLoader Batching and Killing N+1 | a naive @SchemaMapping resolver measured at 21 SQL statements for 20 books versus a @BatchMapping resolver's flat 2, a dangling foreign key nulling an entire GraphQL response via non-null propagation identically under both resolver strategies, and two Spring Boot 4.1 packaging changes (DataSourceAutoConfiguration's new package, Jackson 3 by default) hit along the way |
custom-validation/ |
Custom Validation in Spring Boot: Beyond the Basics! | the javax.validation to jakarta.validation namespace fix Boot 3 already required, Jakarta Validation 3.1's record-validation clarification proven on both field- and class-level custom constraints, a record validation failure's empty 400 body by default, and what spring.mvc.problemdetails.enabled does and does not fix |
etag-caching/ |
Mastering Cache Control with ETag in Spring Boot RESTful APIs | spring-boot-starter-web's own POM now reading "deprecated in favor of spring-boot-starter-webmvc", ShallowEtagHeaderFilter and WebRequest.checkNotModified() re-verified unchanged on Spring Framework 7, deep cache vs shallow cache, and conditional PUT with If-Match as optimistic locking |
restclient-basic-auth/ |
Spring Boot RestTemplate with Basic Auth: A Modern Guide | RestClient with Basic Auth two ways against a real embedded server, {noop} passwords confirmed to emit no runtime warning at all, spring-boot-starter-restclient as its own required Boot 4 module, and the RestClient.exchange() trap covered in depth by the RestTemplate to RestClient migration guide |
Articles whose text is kept here rather than only on the blog have it under
<directory>/post/ — post.md for the body and meta.md for the title, excerpt and
categories.
Running any of them
Each project needs a JDK 25 and Maven 3.9. redis also needs redis-server and redis-cli on the
PATH. docker-images and observability also need
Docker, and kubernetes-deployment Docker plus a Kubernetes cluster; their READMEs list the rest:
cd <directory>
export JAVA_HOME=/path/to/jdk-25
mvn -DskipTests package
./scripts/run-all.sh # regenerate every transcript under docs/output/
Licence
MIT — see LICENSE.