Sprint Boot Explained

Sprint Boot Explained

Auto-Configuration

Mechanism: Spring Boot scans META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports (Boot 2.7+; legacy spring.factories in older versions) for @AutoConfiguration classes on the classpath. Each is conditionally applied based on classpath presence, existing beans, and properties.

@AutoConfiguration
@ConditionalOnClass(DataSource.class)
@ConditionalOnMissingBean(DataSource.class)
@EnableConfigurationProperties(DataSourceProperties.class)
public class DataSourceAutoConfiguration {
    @Bean
    public DataSource dataSource(DataSourceProperties properties) {
        return properties.initializeDataSourceBuilder().build();
    }
}

Key conditional annotations:

Annotation Triggers when
@ConditionalOnClass A specific class is present on the classpath
@ConditionalOnMissingBean No bean of that type already exists in context (lets you override defaults)
@ConditionalOnProperty A property matches a given value (e.g., spring.datasource.enabled=true)
@ConditionalOnWebApplication App is a web app (servlet or reactive)
@ConditionalOnBean Another bean already exists

Architect-level point: auto-configuration classes are ordered with @AutoConfigureAfter/@AutoConfigureBefore to resolve dependency ordering (e.g., DataSourceAutoConfiguration must run before JpaAutoConfiguration). To debug “why is my bean not being created,” run the app with --debug or inspect the ConditionEvaluationReport — it lists every auto-config class and the exact condition that matched or didn’t match. This is the single most useful diagnostic tool for auto-config issues and a strong interview answer to “how would you troubleshoot a missing auto-configured bean.”

You can exclude unwanted auto-configuration explicitly:

@SpringBootApplication(exclude = { SecurityAutoConfiguration.class })

Starter Dependencies

Starters are curated dependency descriptors (spring-boot-starter-*) — POMs with no code, just a tested, version-aligned set of transitive dependencies, eliminating manual version matrix management.

Starter Pulls in
spring-boot-starter-web Spring MVC, embedded Tomcat, Jackson
spring-boot-starter-data-jpa Hibernate, Spring Data JPA, HikariCP
spring-boot-starter-security Spring Security core + config
spring-boot-starter-test JUnit 5, Mockito, AssertJ, Spring Test
spring-boot-starter-actuator Production-ready metrics/health endpoints

Architect-level nuance: version alignment is governed by spring-boot-dependencies BOM (<dependencyManagement> import), not the starter itself. This is why you generally omit <version> tags for Spring-managed dependencies — overriding a transitive version without understanding the BOM’s tested combination is a common source of runtime classpath conflicts (NoSuchMethodError at runtime from version skew). When asked “how do you manage a custom internal library across microservices consistently,” the answer architects should reach for is creating your own internal BOM modeled on this same pattern.


Application Properties & YAML

Two equivalent formats; YAML preferred for hierarchical config readability, .properties for simplicity/grep-ability.

spring:
  datasource:
    url: jdbc:postgresql://localhost:5432/orders
    hikari:
      maximum-pool-size: 20
server:
  port: 8081
  servlet:
    context-path: /api

app:
  feature:
    new-checkout-enabled: true

Equivalent .properties:

spring.datasource.url=jdbc:postgresql://localhost:5432/orders
spring.datasource.hikari.maximum-pool-size=20
server.port=8081

Binding to type-safe config (preferred over @Value for groups of related properties):

@ConfigurationProperties(prefix = "app.feature")
public record FeatureProperties(boolean newCheckoutEnabled) {}

Requires @EnableConfigurationProperties(FeatureProperties.class) or @ConfigurationPropertiesScan.

Property source precedence (highest wins) — frequently asked in architect interviews:

  1. Command-line args
  2. SPRING_APPLICATION_JSON env var
  3. JNDI / Servlet init params
  4. System.getProperties()
  5. OS environment variables
  6. Profile-specific application-{profile}.yml outside the jar
  7. Profile-specific application-{profile}.yml inside the jar
  8. application.yml outside the jar
  9. application.yml inside the jar (packaged default)
  10. @PropertySource annotations
  11. Default properties (SpringApplication.setDefaultProperties)

This ordering is the basis for the standard externalized-config strategy: bake safe defaults into the jar, override via env vars or mounted config in containerized/K8s deployments (12-factor app principle) rather than rebuilding jars per environment.


Profiles (dev/test/prod)

Profiles activate environment-specific bean definitions and property overrides without code branching.

@Configuration
@Profile("dev")
public class DevConfig {
    @Bean
    public DataSource dataSource() { return new EmbeddedH2DataSource(); }
}

@Configuration
@Profile("prod")
public class ProdConfig {
    @Bean
    public DataSource dataSource() { return buildPooledPostgresDataSource(); }
}
# application.yml — common defaults
spring:
  profiles:
    active: dev
---
spring:
  config:
    activate:
      on-profile: prod
server:
  port: 80
logging:
  level:
    root: WARN

Activation methods (in addition to property precedence above): -Dspring.profiles.active=prod, SPRING_PROFILES_ACTIVE=prod env var, or programmatically via SpringApplicationBuilder.

Architect-level nuance — multiple profiles & groups: profiles can be combined (prod,eu-region), and Boot 2.4+ supports spring.profiles.group to bundle several profiles under one activation flag (e.g., activating production auto-activates prod,monitoring,audit-logging). This is the cleaner pattern over giant conditional profile chains for multi-region/multi-tenant deployments — a good answer when asked how you’d structure config for a system deployed across multiple regions/environments (relevant to your Smith & Nephew multi-region delivery context).

Pitfall to flag in reviews: don’t gate security-relevant beans behind @Profile("dev") defaults that silently weaken posture (e.g., disabling CSRF) — explicitly verify the active profile at deploy time via Actuator’s /actuator/env.


Embedded Servers (Tomcat/Jetty/Netty)

Spring Boot bundles the servlet container inside the executable jar — no external app server deployment (no WAR-to-Tomcat-instance ceremony).

Server Type Default for Use case
Tomcat Servlet (blocking, thread-per-request) spring-boot-starter-web (default) General-purpose synchronous REST APIs
Jetty Servlet (blocking, lighter footprint) Swap via excluding Tomcat, adding starter-jetty Lower memory footprint, embedded/IoT-adjacent use
Netty Reactive (non-blocking, event-loop) spring-boot-starter-webflux (default) High-concurrency, I/O-bound, reactive/WebFlux stacks
Undertow Servlet or reactive, very lightweight Swap-in alternative High-throughput, low-overhead

Swapping the default:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <exclusions>
        <exclusion>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jetty</artifactId>
</dependency>

Architect decision criteria (common system-design interview thread):

  • Tomcat/Jetty (servlet, blocking): simpler mental model, mature ecosystem, fine when downstream calls are fast or thread pool sizing covers peak concurrency. Thread-per-request model caps concurrency at thread pool size — a slow downstream dependency exhausts threads under load.
  • Netty (WebFlux, reactive): scales to very high concurrent connections per node with bounded threads (event loop), but requires fully non-blocking I/O end-to-end — mixing in a blocking JDBC call defeats the purpose and blocks the event loop. Steeper learning curve (Mono/Flux, backpressure) and harder debugging (stack traces span async boundaries).

Configuration is unified via server.* properties regardless of which container is active:

server:
  port: 8080
  tomcat:
    threads:
      max: 200
    connection-timeout: 5s

Spring Boot CLI

A command-line tool for rapidly prototyping Spring applications from Groovy scripts — runs Groovy code directly with automatic dependency resolution, without a full Maven/Gradle project structure.

# Install via SDKMAN
sdk install springboot

# Run a single-file Groovy app directly
spring run app.groovy
// app.groovy
@RestController
class HelloController {
    @RequestMapping("/")
    String home() { "Hello from Spring Boot CLI" }
}

CLI auto-detects @Grab-style dependencies and Spring annotations to resolve the classpath without an explicit pom.xml.

Honest architect-level take: Spring Boot CLI is now largely a legacy/niche tool — it’s rarely used in production engineering workflows today. Most teams reach for Spring Initializr (start.spring.io, or its IDE/CLI integrations) to bootstrap a proper Maven/Gradle project structure instead. If this comes up in an interview, it’s worth knowing it exists and what problem it solved (zero-ceremony scripting/prototyping), but flagging that it’s not part of a modern day-to-day toolchain shows current, practical awareness rather than dated knowledge — a good way to differentiate yourself from candidates reciting outdated material.