Chapter 2 β€’ Premium Interview Kit Spring Boot 3.x / Java 21 Standard ⏱️ 30 Min Read

Chapter 2: Spring Boot Architecture & Microservices Internals

Production-grade deep-dive into Spring Boot auto-configuration, 12-step Bean Lifecycle, CGLIB proxies, Saga Pattern, Transactional Outbox, Resilience4j, and HikariCP connection pool tuning.

πŸ‘¨β€πŸ’»
Nagendra Rana
SDE-2 @ Digilytics AI • Lead Architect WhatInfoTech

1. Concept Introduction

Why Spring Boot & Microservices Exist

Prior to Spring Boot (introduced by Pivotal in 2014), enterprise Java development was defined by extreme configuration overhead. Developers spent up to 30% of project lifecycles authoring monolithic XML configurations, managing complex web.xml deployment descriptors, and resolving "JAR dependency hell" across disparate library versions deployed onto external application servers (e.g., IBM WebSphere, Oracle WebLogic, Apache Tomcat).

Simultaneously, monolithic application architectures faced scaling bottlenecks:

  1. Coupled Deployment Pipelines: A single line change required re-testing and re-deploying the entire multi-gigabyte monolithic application.
  2. Resource Inefficiency: Scaling a single memory-heavy domain (e.g., PDF generation) required scaling the entire monolith, wasting CPU and RAM.
  3. Blast Radius Risk: A memory leak or unhandled exception in an auxiliary module (e.g., reporting) brought down core payment processing.

Spring Boot revolutionized Java microservices by establishing two core principles: Convention over Configuration and Self-Contained Executable Starters.

monolith-vs-microservices.txt • Architectural Shift
+-------------------------------------------------------------------------+
|                        MICROSERVICES ERA (SPRING BOOT)                  |
|  +--------------------+  +--------------------+  +-------------------+  |
|  | Auth Microservice  |  | Order Microservice |  | Pay Microservice  |  |
|  | - Spring Boot      |  | - Spring Boot      |  | - Spring Boot     |  |
|  | - Embedded Tomcat  |  | - Embedded Netty   |  | - Embedded Tomcat |  |
|  +--------------------+  +--------------------+  +-------------------+  |
|  | Docker Container   |  | Docker Container   |  | Docker Container  |  |
|  +--------------------+  +--------------------+  +-------------------+  |
|  | Kubernetes Pod     |  | Kubernetes Pod     |  | Kubernetes Pod    |  |
|  +--------------------+  +--------------------+  +-------------------+  |
+-------------------------------------------------------------------------+

Evolution & Modern Ecosystem

  • Spring Boot 1.x (2014): Introduced Auto-Configuration, spring.factories, embedded Tomcat, and basic Actuator endpoints.
  • Spring Boot 2.x (2018): Introduced Spring WebFlux (Reactive Stack powered by Reactor Netty), Java 8/11 baseline, Micrometer metrics, and Spring Cloud Finchley integration.
  • Spring Boot 3.x (2022–Present): Built on Spring Framework 6 and Java 17+. Migrated from javax.* to jakarta.* package namespaces, integrated native AOT (Ahead-Of-Time) compilation with GraalVM Native Images, and standardized distributed tracing via Micrometer Tracing.

2. Theory & Low-Level Internals

A. The 12-Step Spring Bean Lifecycle

bean-lifecycle.txt • Complete 12-Step Initialization & Destruction Flow
1. Bean Definition Loading (.class / @Bean metadata)
β”‚
β–Ό
2. Instantiation (Constructor Invocation / Reflection)
β”‚
β–Ό
3. Populate Properties (Dependency Injection / Field & Setter Injection)
β”‚
β–Ό
4. BeanNameAware / BeanClassLoaderAware / BeanFactoryAware
β”‚
β–Ό
5. BeanPostProcessor.postProcessBeforeInitialization()
β”‚
β–Ό
6. @PostConstruct Annotated Methods
β”‚
β–Ό
7. InitializingBean.afterPropertiesSet()
β”‚
β–Ό
8. Custom init-method (defined in @Bean(initMethod = "..."))
β”‚
β–Ό
9. BeanPostProcessor.postProcessAfterInitialization()  <-- (AOP Proxy Wrapping occurs here)
β”‚
β–Ό
10. BEAN IS READY FOR USE
β”‚
β–Ό (Container Shutdown Triggered)
11. @PreDestroy Annotated Methods / DisposableBean.destroy() / Custom destroy-method

B. Distributed Transactions: Saga Pattern vs Two-Phase Commit (2PC)

In a microservices architecture where each service owns an isolated database, atomic ACID transactions spanning multiple databases via Two-Phase Commit (2PC) introduce prohibitive scaling liabilities:

  • Blocking Locks: Database row locks are held across network boundaries during prepare/commit phases, triggering connection pool exhaustion and low throughput.
  • Single Point of Failure: Transaction Coordinator node failure leaves database transactions in an undecided, locked state.

The Saga Pattern enforces Eventually Consistent distributed transactions through a sequence of local database transactions:

orchestrated-saga-pattern.txt • Compensating Transaction Flow
                              ORCHESTRATED SAGA FLOW

+-------------------+       +-----------------------+       +---------------------+
|   Order Service   |       |  Saga Orchestrator    |       | Payment Service     |
+-------------------+       +-----------------------+       +---------------------+
          β”‚                             β”‚                              β”‚
          β”‚ 1. Create Pending Order     β”‚                              β”‚
          β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€>β”‚                              β”‚
          β”‚                             β”‚ 2. Process Payment           β”‚
          β”‚                             β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€>β”‚
          β”‚                             β”‚                              β”‚
          β”‚                             β”‚ 3. Payment Failed            β”‚
          β”‚                             |<──────────────────────────────
          β”‚                             β”‚                              β”‚
          β”‚                             β”‚ 4. Execute Compensating Tx   β”‚
          β”‚                             β”‚    (Cancel Order)            β”‚
          β”‚                             β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€>β”‚

3. Real Production Architecture Example

High-throughput payment processing architecture handling 50,000 requests/sec with Resilience4j circuit breakers, Transactional Outbox event generation, Redis caching, and Micrometer distributed tracing.

payment-microservices-topology.txt • Resilient High-Throughput Topology
                                EXTERNAL TRAFFIC
                                       β”‚
                                       v
                     +-----------------------------------+
                     |    Spring Cloud API Gateway       |
                     | - OAuth2 / JWT Validation         |
                     | - Token Bucket Rate Limiting      |
                     +-----------------------------------+
                                       β”‚
                                       v
                     +-----------------------------------+
                     |   Payment Processing Microservice |
                     |   (Spring Boot 3.x Engine)        |
                     | - Resilience4j Circuit Breaker    |
                     | - Micrometer Tracing (Trace ID)   |
                     +-----------------------------------+
                                 β”‚           β”‚
                 β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜           └──────────────┐
                 β–Ό                                          β–Ό
+----------------------------------+      +----------------------------------+
|    Redis Distributed Cache       |      |     PostgreSQL Primary DB        |
| - Fast Idempotency Key Lookup    |      | - Payment Ledger Records         |
+----------------------------------+      | - Transactional Outbox Table     |
                                          +----------------------------------+
                                                           β”‚
                                                           β–Ό
                                          +----------------------------------+
                                          | Debezium CDC Engine              |
                                          +----------------------------------+
                                                           β”‚
                                                           β–Ό
                                          +----------------------------------+
                                          | Apache Kafka Cluster             |
                                          +----------------------------------+

4. Code Examples

A. Anti-Pattern: Self-Invocation Transaction Bypass & Blocking WebFlux Event Loops

package com.interviewkit.springboot.bad;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import reactor.core.publisher.Mono;

@Service
public class BadPaymentService {

    private final PaymentRepository repository;

    public BadPaymentService(PaymentRepository repository) {
        this.repository = repository;
    }

    // ANTI-PATTERN 1: Self-invocation of @Transactional method
    public void processOrder(String orderId, double amount) {
        // Internal method invocation bypasses Spring CGLIB Proxy!
        // The @Transactional annotation on saveTransaction() IS IGNORED!
        this.saveTransaction(orderId, amount);
    }

    @Transactional
    public void saveTransaction(String orderId, double amount) {
        repository.save(new PaymentEntity(orderId, amount));
    }

    // ANTI-PATTERN 2: Blocking I/O inside WebFlux / Reactive Event Loop Thread
    public Mono<String> fetchExternalStatusReactive(String transactionId) {
        return Mono.just(transactionId)
            .map(id -> {
                // CRITICAL BUG: Thread.sleep() or blocking RestTemplate call inside
                // Reactor Netty event loop thread freezes the entire server throughput!
                try {
                    Thread.sleep(5000); // Blocks reactor-http-nio thread!
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
                return "SUCCESS";
            });
    }
}

B. Production Pattern: Transactional Outbox Pattern Implementation

package com.interviewkit.springboot.production.outbox;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.Instant;
import java.util.UUID;

record OrderCreatedEvent(String orderId, String customerId, double amount) {}

@Service
public class OrderService {

    private final OrderRepository orderRepository;
    private final OutboxRepository outboxRepository;
    private final ObjectMapper objectMapper;

    public OrderService(OrderRepository orderRepository, OutboxRepository outboxRepository, ObjectMapper objectMapper) {
        this.orderRepository = orderRepository;
        this.outboxRepository = outboxRepository;
        this.objectMapper = objectMapper;
    }

    @Transactional
    public String createOrder(String customerId, double amount) {
        String orderId = UUID.randomUUID().toString();
        
        // 1. Write core business entity
        OrderEntity order = new OrderEntity(orderId, customerId, amount, "PENDING");
        orderRepository.save(order);

        // 2. Write event to Transactional Outbox inside SAME DB transaction
        try {
            String payload = objectMapper.writeValueAsString(new OrderCreatedEvent(orderId, customerId, amount));
            OutboxEntity outboxRecord = new OutboxEntity(
                UUID.randomUUID().toString(),
                "ORDER_EVENTS",
                orderId,
                payload,
                Instant.now()
            );
            outboxRepository.save(outboxRecord);
        } catch (Exception e) {
            throw new IllegalStateException("Failed to serialize Outbox event", e);
        }

        return orderId;
    }
}

5. Tiered Interview Questions

Medium Level • Mid-Senior SDE

Q1: How does Spring Boot auto-configuration work under the hood without triggering ClassNotFoundException?

Ideal Answer: `@EnableAutoConfiguration` imports `AutoConfigurationImportSelector` which reads candidate auto-configuration classes from `META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports`. `@ConditionalOnClass` uses the ASM bytecode library to inspect candidate target classes without calling `Class.forName()`, preventing `ClassNotFoundException` when optional dependencies are absent.

Expert Level • Principal Architect

Q2: Spring Cloud Gateway Netty vs Tomcat Threading Model comparison

Ideal Answer: Tomcat uses Thread-per-Request model (default 200 worker threads); blocking downstream calls exhaust the thread pool. Spring Cloud Gateway on Netty uses Non-Blocking Reactive EventLoop model (`epoll`/`kqueue`) allocating $2 \times \text{CPU Cores}$ threads to handle tens of thousands of concurrent active connections.

6. Production Debugging Scenario

Symptom: HikariCP Connection Pool Exhaustion (504 Gateway Timeout)

Root Cause: External 10-second REST call executed INSIDE `@Transactional` boundary held active HikariCP connection locked for 10 seconds, exhausting connection pool.

Resolution: Execute network REST call OUTSIDE database transaction boundary, using short separate transactions for DB reads/writes.

Official Partner Verified Opportunity

πŸ“œ Official Developer Certification & Cloud Hosting

Validate your software engineering skills with official developer certifications and high-performance cloud hosting.

β˜• Support My Work