How Spring Boot Applications Really Work Under the Hood


Spring Boot has become the standard for building standalone, production ready Java applications with almost zero configuration. You write a single main() method, include a handful of “starters,” and get an embedded server, auto configured beans, health checks, metrics, and more. But what exactly happens from the moment you call SpringApplication.run(...) to when your REST endpoint responds? And where does Inversion of Control (IOC) fit in? Let’s unravel the internals, layer by layer, with clear Mermaid diagrams to guide you.


{getToc} $title={Table of Contents} $count={true} $expanded={false}

How Spring Boot Kicks Off

One of Spring Boot’s biggest draws is its auto configuration magic. Rather than wrestling with XML files, you simply declare the “starter” dependencies your app needs, and Spring Boot wires up everything behind the scenes. For example, adding the JPA starter in your pom.xml

<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency>

The above dependency brings in a complete, ready to use JPA setup: DataSource, EntityManagerFactory, JpaTransactionManager, and all the required Hibernate settings. From there, you can focus on defining your entities, repositories, and service logic, while Spring Boot handles connection pooling, SQL dialects, and transaction configuration automatically

Key Annotations to Bootstrap Your Spring Boot App

Every Spring Boot application needs an entry point class annotated with @SpringBootApplication and a main method that calls SpringApplication.run(). Behind the scenes, @SpringBootApplication bundles three core annotations:
import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class MyApp { public static void main(String[] args) { SpringApplication.run(MyApp.class, args); } }


What @SpringBootApplication Actually Does

@SpringBootApplication // is equivalent to: @Configuration @EnableAutoConfiguration @ComponentScan


@Configuration
Marks the class as a source of bean definitions. Any @Bean methods you declare here will be registered in the application context.

@Configuration public class AppConfig { @Bean public DemoService demoService() { return new DemoService(); } }


@EnableAutoConfiguration

Instructs Spring Boot to automatically configure your application based on the JAR dependencies you’ve included. For example, if you have spring-boot-starter-data-jpa on the classpath, Spring Boot will set up a DataSource, JPA EntityManagerFactory, a transaction manager, and default Hibernate settings without any XML or manual setup.


@ComponentScan

Tells Spring to scan for other components, configurations, and services in the package where your application class resides (and its subpackages). Any @Component, @Service, @Repository, or @Controller you’ve defined will be discovered and registered automatically.

By combining these three annotations into a single @SpringBootApplication, you get all of their behaviors in one place. This “one stop” annotation makes Spring Boot straightforward: declare your starters, annotate your main class, and watch the application context wire itself up with minimal boilerplate.

Layered Architecture at a Glance

Most Spring Boot apps organize into four logical layers:

flowchart TD A[Presentation] --> B[Business] B --> C[Persistence] C --> D[Database]

Presentation Layer
Handles incoming HTTP requests, deserializes JSON/form data into Java objects, applies authentication/validation, and forwards calls to the business layer.

Business Layer
Contains domain logic services, validation, orchestration of multiple repositories, and transaction boundaries.

Persistence Layer
Translates Java objects into database operations via JPA, JDBC, or other data access APIs.

Database Layer
The actual data store (relational or NoSQL), where CRUD operations insert, query, update, or delete data.

Startup Flow Overview

Here’s the end to end sequence from your main() entry point to a live, request ready application:
flowchart TD subgraph Bootstrap A[main] --> B[SpringApplication.run] B --> C[Environment Preparation] C --> D[Banner & Listeners] end subgraph Context Creation D --> E[Auto-Configuration] E --> F[Bean Definition Scan] F --> G[Instantiate & Inject Beans] G --> H[Context Refresh Complete] end subgraph Server Startup H --> I[Embedded Server Initialization] I --> J[DispatcherServlet Ready] end
  1. main() & run(...)
    • The class annotated with @SpringBootApplication invokes SpringApplication.run().
    • Spring Boot loads configuration sources (properties, YAML, env vars) and notifies any ApplicationListeners.
    • It prints the startup banner (customizable).
  2. Auto Configuration
    • Driven by @EnableAutoConfiguration, Spring Boot examines your classpath and defines beans conditionally:
      • Web starter → embedded Tomcat, Spring MVC, Jackson
      • Data JPA starter → DataSource, EntityManagerFactory, JpaTransactionManager
  3. Creating the ApplicationContext
    • Spring scans for stereotypes (@Component, @Service, @Repository, @Controller) and @Configuration classes.
    • It builds a dependency graph, instantiates beans in dependency order, and applies any BeanPostProcessor hooks.
  4. Embedded Server Initialization
    • A ServletWebServerFactory starts an embedded servlet container (Tomcat/Jetty/Undertow) on your configured port (8080 by default).
    • It deploys the DispatcherServlet and any servlet filters you’ve configured.


Inversion of Control (IOC) & Dependency Injection

At the heart of Spring’s magic sits the IOC container. Instead of your code instantiating dependencies, the container injects them for you. This promotes loose coupling, testability, and clear separation of concerns.

flowchart TD subgraph "IOC Container" beanDefs[Bean Definitions] --> instantiate[Instantiate Beans] instantiate --> resolve[Resolve Dependencies] resolve --> inject[Inject into Consumers] inject --> ready[Application Ready] end subgraph "Your Code" OC[OrderController] -->|"@Autowired"| OS[OrderService] OS -->|"@Autowired"| OR[OrderRepository] end OC --> beanDefs OS --> beanDefs OR --> beanDefs
  1. Bean Definitions
    • Parsed from @Component, @Service, @Repository, @Controller, and @Bean methods in @Configuration classes.
  2. Instantiate Beans
    • The container creates each bean instance, respecting singleton or prototype scope.
  3. Resolve Dependencies
    • For each bean, Spring analyzes constructor parameters, fields, or setter methods annotated with @Autowired, @Inject, or @Value.
  4. Inject into Consumers
    • Dependencies are injected before any bean initialization callbacks (like @PostConstruct) run.


Detailed Layer Interactions

1. Presentation Layer

  • Controllers (@RestController / @Controller) map HTTP endpoints to handler methods.
  • Argument resolvers bind path variables, request parameters, and request bodies.
  • Exception handlers (@ControllerAdvice) convert exceptions into HTTP error responses.

2. Business Layer

  • Service classes encapsulate business rules, orchestrating calls to multiple repositories or external systems.
  • Transactions are managed via @Transactional, ensuring atomicity and consistency.

3. Persistence Layer

  • Spring Data JPA repositories provide CRUD methods out of the box.
  • Custom queries via @Query annotations or the Criteria API.
  • Entity lifecycle: JPA entities are tracked in the persistence context until the transaction commits.

4. Database Layer

  • Executes SQL (or NoSQL operations) against your chosen data store.
  • Connection pooling (HikariCP by default) optimizes resource usage.

A Real Request Sequence

Imagine a client issues a POST /api/orders:

sequenceDiagram participant C as Client participant S as Spring Boot App participant DB as Database C->>S: POST /api/orders S->>S: DispatcherServlet routes to OrderController S->>S: OrderService.processOrder() S->>DB: INSERT into orders DB-->>S: Success S-->>C: 201 Created (with Location header)
  1. DispatcherServlet routes the request to OrderController.
  2. OrderService applies validation, inventory checks, and other business rules.
  3. OrderRepository persists the entity.
  4. Transaction commits, flushing SQL to the database.
  5. Controller returns a 201 Created with the new order’s URI.


Why You Should Care

  • Troubleshooting: When bean conflicts or startup errors occur, you’ll know where to look.
  • Customization: You can exclude or override auto configurations, add your own ApplicationContextInitializer, or define custom BeanFactoryPostProcessors.
  • Performance: Disabling unused auto configs, tuning thread pools, and tweaking HikariCP settings can make startup and runtime snappier.

Spring Boot isn’t a magical black box. Underneath its elegant API lies a well orchestrated startup process, a powerful IOC container, and layered architecture that together let you focus on business logic, not boilerplate. Armed with these diagrams and explanations, you’ll never look at mvn spring-boot:run the same way again.