Java Fundamentals Tutorial 0/33 lessons ~6 min read Lesson 21

    Spring Boot

    Spring Boot is how enterprises ship Java services in 2026 — not a replacement for Spring Core, but an opinionated layer that auto-wires datasources, Jackson, Tomcat, and Microme…

    Course progress0%
    Focus
    21 guided sections
    Practice signal
    Examples included
    Career prep
    Interview Q&A included

    Introduction

    Spring Boot is how enterprises ship Java services in 2026 — not a replacement for Spring Core, but an opinionated layer that auto-wires datasources, Jackson, Tomcat, and Micrometer from classpath + properties. A @SpringBootApplication class and a spring-boot-starter-web dependency give you a production HTTP server in minutes; the same stack without Boot requires hundreds of lines of XML or Java config.

    This lesson covers auto-configuration (how @Conditional beans activate from classpath), starter dependencies (curated BOMs that pull compatible versions), Spring Boot Actuator (health, metrics, readiness for Kubernetes), and profiles (dev/staging/prod configuration without code forks).

    Staff engineers treat Boot as infrastructure-as-code: understand condition evaluation, override defaults safely, and expose the right actuator endpoints — not blindly add starters until the fat JAR exceeds 200 MB.

    Business problem

    Spring Boot misuse creates operational debt:

    • Fat JAR bloat: Adding spring-boot-starter-* without understanding transitive deps — 300 MB images, slow K8s pulls.
    • Magic failures: Auto-config silently skipped — "works on my laptop" because local H2 on classpath, prod PostgreSQL missing driver bean.
    • Actuator exposure: /actuator/env public on internet — credentials leaked in incident report.
    • Profile drift: application-prod.yml missing new property — staging passes, prod defaults to insecure value.
    • Startup regressions: Spring Boot 3.x upgrade breaks javax → jakarta — no condition report review before deploy.

    Why this topic exists

    Spring Boot solves the "getting to production" gap:

    • Convention over configuration: Sensible defaults for embedded Tomcat, Jackson, Logback, HikariCP.
    • Dependency management: spring-boot-dependencies BOM aligns Spring, Hibernate, Netty versions — no dependency hell.
    • Executable JAR: java -jar app.jar — container-friendly deploy without external app server.
    • Observability built-in: Actuator + Micrometer — health probes, Prometheus scrape, startup timeline.
    • Environment parity: Profiles + externalized config — same artifact, different application-{profile}.yml.

    Core concepts

    Four Spring Boot pillars:

    • Auto-configuration: @AutoConfiguration classes register beans when @ConditionalOnClass, @ConditionalOnMissingBean, @ConditionalOnProperty match — e.g. DataSource when JDBC on classpath.
    • Starters: spring-boot-starter-web, starter-data-jpa, starter-actuator — thin POMs aggregating dependencies + auto-config.
    • Actuator: Production endpoints — /health, /metrics, /info, /env — secured and exposed selectively.
    • Profiles: spring.profiles.active=prod loads application-prod.yml; @Profile("prod") on beans.
    • Externalized config: Properties hierarchy — command line > env vars > application.yml > defaults.

    Internal architecture

    Spring Boot startup and auto-config evaluation:

    text
    SpringApplication.run(App.class)
    Load application.yml + application-{profile}.yml
    @SpringBootApplication
    = @Configuration + @EnableAutoConfiguration + @ComponentScan
    AutoConfigurationImportSelector
    reads META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
    For each @AutoConfiguration class:
    @ConditionalOnClass? @ConditionalOnProperty? @ConditionalOnMissingBean?
    ├── MATCH → register beans (DataSource, JpaVendor, etc.)
    └── NO MATCH → skip (logged in DEBUG)
    Actuator (management.endpoints):
    /actuator/health → liveness/readiness (K8s probes)
    /actuator/metrics → Micrometer registry
    /actuator/prometheus → scrape endpoint

    Auto-configuration, starters, actuator, and profiles:

    Auto-configuration decision
    Classpath
    JDBC present?
    @Conditional
    Rules
    DataSource bean
    HikariCP
    Or skip
    Log negative match
    Beans appear when classpath and properties satisfy conditions.
    Starter dependency chain
    starter-web
    Your POM
    spring-webmvc
    Transitive
    tomcat-embed
    Transitive
    jackson
    Transitive
    Starters bundle tested dependency sets — one line in build.gradle.
    Kubernetes probes
    liveness
    /health/liveness
    readiness
    /health/readiness
    Pod
    Traffic when ready
    Restart
    If liveness fails
    Actuator health groups map directly to K8s probe config.
    Profile activation
    SPRING_PROFILES_ACTIVE
    prod
    application-prod.yml
    Overrides
    @Profile beans
    Conditional wiring
    Same JAR
    All environments
    One artifact — profile selects configuration layer.

    Code walkthrough

    Spring Boot application — auto-config, profiles, actuator:

    • @SpringBootApplication: Enables auto-config scan — DataSource appears when JDBC + Hikari on classpath.
    • ddl-auto: validate: Flyway/Liquibase owns schema — Hibernate never mutates prod tables.
    • health probes: K8s uses /actuator/health/liveness and /readiness.
    • @ConfigurationProperties: Type-safe binding vs scattered @Value injections.
    java
    // build.gradle.kts
    plugins { id("org.springframework.boot") version "3.4.0" }
    dependencies {
    implementation("org.springframework.boot:spring-boot-starter-web")
    implementation("org.springframework.boot:spring-boot-starter-data-jpa")
    implementation("org.springframework.boot:spring-boot-starter-actuator")
    runtimeOnly("org.postgresql:postgresql")
    }
    @SpringBootApplication
    public class PaymentApplication {
    public static void main(String[] args) {
    SpringApplication.run(PaymentApplication.class, args);
    }
    }
    // application.yml
    spring:
    application:
    name: payment-service
    datasource:
    url: ${DB_URL:jdbc:postgresql://localhost:5432/payments}
    hikari:
    maximum-pool-size: 20
    jpa:
    hibernate:
    ddl-auto: validate
    profiles:
    active: ${SPRING_PROFILES_ACTIVE:dev}
    management:
    endpoints:
    web:
    exposure:
    include: health,metrics,prometheus,info
    endpoint:
    health:
    probes:
    enabled: true
    metrics:
    tags:
    application: ${spring.application.name}
    // application-prod.yml — overrides only prod-specific
    spring:
    jpa:
    show-sql: false
    logging:
    level:
    root: WARN
    // Custom auto-config override
    @Configuration
    public class DataSourceConfig {
    @Bean
    @ConfigurationProperties("spring.datasource.hikari")
    public HikariDataSource dataSource(DataSourceProperties props) {
    return props.initializeDataSourceBuilder().type(HikariDataSource.class).build();
    }
    }

    Production example

    Production Spring Boot deploy — K8s + Actuator + profiles:

    • Non-root container: USER 1000 — security baseline for regulated workloads.
    • Secrets via env: Never commit prod credentials — K8s Secret → SPRING_APPLICATION_JSON or env.
    • Probe separation: Readiness checks DB; liveness only JVM up — avoid restart loops on DB blip.
    • Actuator security: Public health only — lock /env and /heapdump.
    bash
    # Dockerfile
    FROM eclipse-temurin:21-jre-alpine
    COPY build/libs/payment-service.jar /app.jar
    USER 1000
    ENTRYPOINT ["java", "-XX:+UseZGC", "-jar", "/app.jar"]
    # kubernetes deployment snippet
    env:
    - name: SPRING_PROFILES_ACTIVE
    value: prod
    - name: DB_URL
    valueFrom:
    secretKeyRef: { name: db-credentials, key: url }
    livenessProbe:
    httpGet: { path: /actuator/health/liveness, port: 8080 }
    initialDelaySeconds: 30
    readinessProbe:
    httpGet: { path: /actuator/health/readiness, port: 8080 }
    # Secure actuator — Spring Security
    @Bean
    SecurityFilterChain actuatorSecurity(HttpSecurity http) {
    return http.securityMatcher("/actuator/**")
    .authorizeHttpRequests(a -> a.requestMatchers("/actuator/health/**").permitAll()
    .anyRequest().hasRole("OPS"))
    .build();
    }

    Enterprise case study

    Streaming platform — Actuator leak and startup failure: A media company exposed all actuator endpoints on public ALB during a "quick debug." Attackers scraped /actuator/env containing AWS keys. Parallel incident: Spring Boot 3 migration — auto-config for Redis skipped because wrong starter (spring-data-redis without boot starter) — cache silently disabled, DB overload. Fixes: Spring Security actuator matcher, explicit spring-boot-starter-data-redis, and CI gate running with debug profile to print negative auto-config matches.

    • Actuator: Expose health/prometheus only; IP allowlist for admin endpoints.
    • Auto-config: Document required starters per module in archetype template.
    • Upgrade: Use Boot 3 migration guide — jakarta.* imports, spring-security 6.

    Performance considerations

    Spring Boot performance:

    • Cold start: Lazy initialization (spring.main.lazy-initialization=true) trades first-request latency for faster startup — use cautiously.
    • Classpath scanning: Narrow @SpringBootApplication(scanBasePackages) — faster startup in monoliths.
    • DevTools: Never in production — classloader restart overhead and security risk.
    • Actuator overhead: Micrometer cardinality explosion — limit custom tag dimensions.
    • Native image: GraalVM AOT for serverless — longer build, faster cold start.

    Security considerations

    Spring Boot security checklist:

    • Actuator lockdown: Default Boot 3 exposes limited endpoints — verify management.endpoints.web.exposure.
    • Actuator on separate port: management.server.port=9090 — network policy isolation.
    • Dependency scanning: OWASP dependency-check in CI — Boot BOM still pulls transitive CVEs.
    • spring.config.import: Vault/AWS Secrets Manager — no secrets in git-tracked yml.

    Scalability considerations

    Scaling Boot services:

    • Stateless pods: No local session state — scale HPA on CPU or custom metric from Actuator.
    • Graceful shutdown: server.shutdown=graceful — drain in-flight requests before K8s SIGTERM.
    • Config refresh: Spring Cloud Config optional — env vars + K8s ConfigMaps simpler for many teams.
    • Virtual threads: spring.threads.virtual.enabled=true (Boot 3.2+) — I/O scale without WebFlux.

    Production challenges

    Common Spring Boot production issues:

    • Port already in use: Multiple apps default 8080 — set server.port or K8s service mapping.
    • DataSource pool exhausted: Hikari defaults too small for virtual threads — tune max pool vs thread count.
    • Missing @EnableScheduling/@EnableAsync: Annotations present but feature inactive — starter doesn't enable all.
    • Logback config not picked up: logback-spring.xml not logback.xml for profile sections.

    Common mistakes

    • Adding starters without checking transitive deps — use `gradle dependencies` or `mvn dependency:tree`.
    • Relying on spring.jpa.hibernate.ddl-auto=update in prod — use Flyway/Liquibase.
    • Exposing /actuator/env and /actuator/beans publicly.
    • Hardcoding profile-specific logic in Java instead of configuration files.
    • Ignoring auto-config report after Boot version upgrade.

    Debugging guide

    Debug Spring Boot:

    • Auto-config report: Run with --debug or enable ConditionEvaluationReportLoggingListener.
    • Actuator conditions: /actuator/conditions shows why beans skipped.
    • Failed to configure DataSource: Usually missing url/driver — check profile-specific yml.
    • Startup timeline: /actuator/startup or Flight Recorder — find slow bean init.
    bash
    java -jar app.jar --debug 2>&1 | grep -A2 "Negative matches"
    curl -s localhost:8080/actuator/conditions | jq '.contexts.application.conditions'
    # Which profile active?
    curl localhost:8080/actuator/env/spring.profiles.active

    Best practices

    • Use Spring Boot BOM — avoid overriding managed versions unless CVE requires it.
    • Externalize all environment-specific values — profiles + env vars + secrets manager.
    • Enable health probe groups for Kubernetes — separate liveness from readiness checks.
    • Pin Boot plugin version in build file — reproducible builds across team.
    • Run OWASP dependency check in CI — starters don't eliminate CVE monitoring.
    • Use `spring-boot-starter-test` with Testcontainers for integration parity.

    Anti-patterns

    • Copy-pasting entire application.yml from tutorial — understand each property.
    • Multiple main classes without clear module boundaries.
    • Disabling all auto-config (@EnableAutoConfiguration(exclude=...)) without documenting why.
    • Using DevTools and spring.jpa.show-sql=true in production images.
    • Custom parent POM that fights Boot dependency management.

    Staff engineer notes

    • Read the auto-config negative match report once per upgrade — it is the fastest Boot debug tool.
    • Starters are contracts — if you need Redis, use the Boot starter, not raw spring-data-redis alone.
    • Actuator is your production API — design exposure with same rigor as REST endpoints.
    • Profiles are not environments — they are configuration slices; same code, different properties.
    • Graceful shutdown + K8s terminationGracePeriodSeconds — prevent payment TX lost mid-flight.

    Interview questions

    Interview preparation

    15 questions grouped by difficulty — expand any card for a model answer and follow-up probe.

    Beginner

    5
    1. 1What is Spring Boot auto-configuration?
      Beginner

      Model answer

      Mechanism that automatically registers beans based on classpath, existing beans, and property values.

      @AutoConfiguration classes use @ConditionalOn* annotations.

      Example: spring-boot-starter-jdbc triggers DataSourceAutoConfiguration when DataSource class and JDBC driver present.

      Reduces boilerplate vs manual @Configuration.

      Follow-up probe

      How disable one auto-config?

    2. 2What are Spring Boot starters?
      Beginner

      Model answer

      • Convenience dependency descriptors
      • e.g. spring-boot-starter-web pulls spring-webmvc, tomcat-embed, jackson, validation. Starters bundle compatible versions via spring-boot-dependencies BOM. You add one starter instead of listing 10 coordinates.

      Follow-up probe

      Create custom starter?

    3. 3What is Spring Boot Actuator?
      Beginner

      Model answer

      • Production-ready features module
      • health checks, metrics (Micrometer), env info, thread dump, heap dump. Endpoints under /actuator/*. Expose selectively via management.endpoints.web.exposure.include. K8s uses health/liveness and health/readiness probe groups.

      Follow-up probe

      Secure actuator?

    4. 4Explain Spring profiles.
      Beginner

      Model answer

      • Logical environment labels
      • spring.profiles.active=prod loads application-prod.yml and activates @Profile('prod') beans. Allows same JAR across dev/staging/prod with different config. Multiple profiles comma-separated. Default profile if none set.

      Follow-up probe

      Profile vs property?

    5. 5Difference @SpringBootApplication and @Configuration?
      Beginner

      Model answer

      @SpringBootApplication = @Configuration + @EnableAutoConfiguration + @ComponentScan (defaults to package of main class).

      Entry point for Boot apps.

      @Configuration alone defines @Bean methods without enabling auto-config or scan unless added explicitly.

      Follow-up probe

      scanBasePackages?

    Intermediate

    5
    1. 6How override auto-configured bean?
      Intermediate

      Model answer

      • Define your own @Bean of same type
      • @ConditionalOnMissingBean on auto-config skips default. Or exclude auto-config class via @EnableAutoConfiguration(exclude=DataSourceAutoConfiguration.class). Or override properties: spring.datasource.hikari.maximum-pool-size.

      Follow-up probe

      @Primary vs override?

    2. 7Property resolution order in Spring Boot?
      Intermediate

      Model answer

      • Command line args highest, then SPRING_APPLICATION_JSON env, OS env vars, application-{profile}.yml, application.yml, @PropertySource, defaults in @ConfigurationProperties. Later sources in same tier don't override earlier
      • specific profile file overrides base application.yml.

      Follow-up probe

      @ConfigurationProperties vs @Value?

    3. 8Configure Kubernetes liveness vs readiness?
      Intermediate

      Model answer

      • management.endpoint.health.probes.enabled=true exposes /actuator/health/liveness (JVM alive, no deadlock) and /readiness (can accept traffic
      • DB up). Liveness failure restarts pod; readiness failure removes from service endpoints. Don't put slow DB check on liveness.

      Follow-up probe

      Custom HealthIndicator?

    4. 9What is spring-boot-starter-parent / BOM?
      Intermediate

      Model answer

      • Dependency management POM
      • pins versions for Spring ecosystem libraries. Gradle: io.spring.dependency-management plugin imports spring-boot-dependencies BOM. Ensures compatible Hibernate, Jackson, Netty versions without manual alignment.

      Follow-up probe

      Upgrade Boot safely?

    5. 10Why ddl-auto=validate in production?
      Intermediate

      Model answer

      • Hibernate checks schema matches entities but never ALTERs tables. Schema migrations owned by Flyway/Liquibase
      • auditable, repeatable, reviewable. update/create-drop risk data loss and untracked DDL in prod.

      Follow-up probe

      Flyway vs Liquibase?

    Advanced

    5
    1. 11Design multi-tenant config with profiles.
      Advanced

      Model answer

      Option A: one profile per tenant (doesn't scale).

      Option B: single prod profile, tenant config in DB/Config Server.

      import for tenant-specific property files.

      Prefer external config service for 100+ tenants; profiles for environment (dev/staging/prod) only.

      Follow-up probe

      Spring Cloud Config?

    2. 12Debug 'DataSource bean not found' at startup.
      Advanced

      Model answer

      Check: JDBC driver on classpath?

      url set for active profile?

      Excluded DataSourceAutoConfiguration?

      Using wrong starter (jdbc vs jpa)?

      Run --debug for negative matches.

      Test with @DataJpaTest to isolate.

      Follow-up probe

      Multiple datasources?

    3. 13Compare Spring Boot 2 vs 3 migration impacts.
      Advanced

      Model answer

      * namespace.

      Spring Security 6 lambda DSL.

      Minimum Java 17.

      PathPatternParser default.

      Removed deprecated classes.

      Actuator endpoint changes.

      Review dependency tree, run migration tool, update imports, test security config.

      Follow-up probe

      Spring Boot 3 + Java 21?

    4. 14Optimize Spring Boot cold start for serverless.
      Advanced

      Model answer

      Lazy init (careful), AOT/native-image with GraalVM, reduce classpath scanning, exclude unused auto-config, smaller starters (exclude tomcat if using lambda web adapter), tiered compilation.

      Measure with /actuator/startup or JFR.

      Follow-up probe

      Native image trade-offs?

    5. 15Architect observability for 50 Boot microservices.
      Advanced

      Model answer

      Standardize: micrometer-registry-prometheus, actuator exposure, consistent metric tags (application, env).

      OpenTelemetry agent for traces.

      Structured JSON logging with traceId.

      Platform team provides base starter-parent with security + actuator defaults.

      Alert on health DOWN and error rate SLO.

      Follow-up probe

      Micrometer vs Dropwizard?

    Hands-on exercise

    Lab: Spring Boot essentials

    • Create Boot app with starter-web + actuator — verify /actuator/health.
    • Add application-dev.yml and application-prod.yml — toggle logging level via profile.
    • Run with --debug — list which auto-configurations matched.
    • Add custom HealthIndicator for external dependency — wire to readiness probe.
    • Configure management.endpoints — expose only health and prometheus.

    JavaSpring Boot: Auto Configuration, Starter Dependencies, Actuator, Profiles

    Starter Templates
    OutputRemote JVM (Piston · Java 15)
    Press Run to execute. Real javac + JVM via remote API. Falls back to simulator on network failure.

    Architecture trade-offs

    • Auto-config vs explicit config: Auto-config speeds dev; explicit when you need full control or audit.
    • Monolith vs microservices: Boot supports both — microservices add network ops cost.
    • Embedded Tomcat vs external: Embedded standard for K8s; external Tomcat legacy enterprise.
    • Properties vs YAML: YAML readable for nested config; properties simpler for K8s env mapping.

    Summary

    Spring Boot turns Spring Core into a production-ready platform — auto-configuration, starters, Actuator, and profiles are the operational toolkit every Java engineer uses daily. Understand condition evaluation and configuration hierarchy to debug startup failures and deploy safely to Kubernetes. Next: REST API design with controllers, DTOs, validation, and OpenAPI.

    Key takeaways

    • Spring Boot auto-configures beans from classpath + properties — read condition report when debugging.
    • Starters bundle tested dependencies — use Boot starters, not raw Spring modules alone.
    • Actuator powers K8s probes and metrics — expose minimally and secure admin endpoints.
    • Profiles externalize environment config — same JAR, different application-{profile}.yml.
    • Production: validate schema, graceful shutdown, lock down actuator, scan dependencies for CVEs.
    Ready to mark this lesson complete?Track your journey across the entire course.