Skip to content

Week 17 - Spring Boot 3 and Quarkus

Conceptual Core

Spring Boot is the de facto JVM web framework: vast ecosystem, opinionated defaults, deep autoconfiguration. Quarkus is the modern challenger: designed around GraalVM native-image from day one, faster cold-starts, leaner footprint. You should know both well enough to choose, and choose for reasons, not vibes.

The trade is roughly: - Spring Boot wins on ecosystem breadth, hireability, and time-to-first-working-feature for any well-known integration (databases, queues, OAuth, anything off-the-shelf). - Quarkus wins on cold start (~30ms native vs ~3s JVM), RSS (~30MB native vs ~250MB JVM), and consistency of dev-loop (live-reload built in, not retro-fitted via DevTools).

Mechanical Detail

  • Spring Boot 3 uses Jakarta EE 9+ namespaces - jakarta.servlet.*, not javax.servlet.*. This break stranded every pre-2022 library that hadn't migrated; verify your transitives.
  • Spring autoconfiguration: starter dependencies (spring-boot-starter-web) pull in defaults; @ConditionalOn* annotations gate beans on classpath / property / missing-bean conditions. application.yml with profiles (spring.profiles.active=prod). Prefer @ConfigurationProperties (typed) over @Value (untyped) for non-trivial config. Slice tests (@WebMvcTest, @DataJpaTest) load only the relevant slice - much faster than @SpringBootTest.
  • Spring's three controller models: blocking MVC (now on virtual threads since 3.2 with spring.threads.virtual.enabled=true - almost always the right default for new projects), reactive WebFlux (Reactor-based; pick only if you need explicit backpressure or operator fusion over streams), RouterFunction DSL (functional alternative to @Controller).
  • Quarkus: build-time DI/AOP - the design choice that makes native-image tractable (reflection metadata is resolved at build, not runtime). @QuarkusTest, live reload via ./mvnw quarkus:dev, the extensions catalog (quarkus-jdbc-postgresql, quarkus-resteasy-reactive, ...). Native build: ./mvnw package -Pnative.
  • Micronaut (briefly): third option, same build-time-DI philosophy as Quarkus. Smaller ecosystem; pick if you specifically need its features (compile-time AOP, GraalVM-first cloud SDK integrations).
  • Honest 2026 default: Spring Boot on virtual threads for established teams; Quarkus for greenfield + serverless + native-image.

The trap

Enabling spring.threads.virtual.enabled=true while still using synchronized blocks that wrap I/O. Pre-JDK 24, that pins the virtual thread to its carrier and erases the benefit (see Week 15). Audit your dependencies - JDBC drivers were notable offenders until ~2024.

Lab

Build the same small REST service (book CRUD, JSON over HTTP, Postgres backend) in both Spring Boot 3 and Quarkus. Measure: - Build time (time ./mvnw package) - Image size (du -sh target/quarkus-app vs the Spring jar) - Cold start (time-to-first-response after launch) - Warm p99 latency under load (k6 or wrk, 100 RPS for 60s) - RSS at steady state (ps -o rss -p $(pgrep -f yourapp))

Document the trade in a one-pager.

Idiomatic Drill

Read the autoconfiguration source for one Spring starter (spring-boot-starter-web is the canonical one). Trace how @SpringBootApplication discovers it via META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports, and how @ConditionalOnMissingBean lets your @Bean override the default.

Production Hardening Slice

Add Spring Boot Actuator endpoints to your template: - /actuator/health (liveness + readiness probes) - /actuator/metrics + /actuator/prometheus (Micrometer + Prometheus scrape) - /actuator/info (build info - git.properties, META-INF/build-info.properties)

Quarkus equivalents: quarkus-smallrye-health, quarkus-micrometer-registry-prometheus, quarkus-info.

Lock down Actuator: expose only health and info publicly; gate metrics, env, loggers behind authentication or a private network.

Comments