← Back to all projects

SPRING FRAMEWORK DEEP DIVE PROJECTS

Learn Spring Framework: From Internals to Mastery

Goal: Deeply understand how Spring works under the hood—IoC containers, dependency injection, AOP proxies, auto-configuration magic—and how it compares to other web frameworks across languages.


Why Understanding Spring Internals Matters

Spring is arguably the most sophisticated application framework ever built. It powers a massive portion of enterprise Java applications, and for good reason—it elegantly solves the fundamental problem of managing complex object graphs in large applications.

But most developers use Spring as a “magic box.” They annotate classes with @Service, @Autowired, @Transactional, and things “just work.” This is fine until:

  • Something breaks and you have no idea why
  • Performance issues arise and you don’t know where to look
  • You need to extend or customize Spring’s behavior
  • You’re asked how Spring works in an interview

After completing these projects, you will:

  • Understand exactly what happens when Spring starts up
  • Know how dependency injection works at the bytecode level
  • Be able to explain AOP proxies (JDK Dynamic vs CGLIB)
  • Understand DispatcherServlet’s request processing pipeline
  • Know what auto-configuration does and how to customize it
  • Compare Spring intelligently with other frameworks
  • Debug Spring applications with confidence

Core Concept Analysis

The Spring Philosophy

┌─────────────────────────────────────────────────────────────────────┐
│                          YOUR APPLICATION                           │
│  ┌──────────────────────────────────────────────────────────────┐  │
│  │                    Business Logic (Your Code)                 │  │
│  └──────────────────────────────────────────────────────────────┘  │
│                               ↓                                     │
│  ┌──────────────────────────────────────────────────────────────┐  │
│  │                  Spring IoC Container                         │  │
│  │  • Creates objects (beans)                                    │  │
│  │  • Wires dependencies                                         │  │
│  │  • Manages lifecycle                                          │  │
│  │  • Applies cross-cutting concerns (AOP)                       │  │
│  └──────────────────────────────────────────────────────────────┘  │
│                               ↓                                     │
│  ┌──────────────────────────────────────────────────────────────┐  │
│  │                    Infrastructure                              │  │
│  │  • Database connections                                        │  │
│  │  • Web servers                                                 │  │
│  │  • Message queues                                              │  │
│  └──────────────────────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────────────────┘

Fundamental Concepts

  1. Inversion of Control (IoC)
    • Traditional: Your code creates its dependencies
    • IoC: A container creates dependencies and gives them to your code
    • “Hollywood Principle”: Don’t call us, we’ll call you
  2. Dependency Injection (DI)
    • Constructor Injection: Dependencies passed via constructor
    • Setter Injection: Dependencies set via setter methods
    • Field Injection: Dependencies injected directly into fields (via reflection)
  3. BeanFactory vs ApplicationContext
    • BeanFactory: Basic container, lazy initialization
    • ApplicationContext: Full-featured, eager initialization, event publishing, AOP
  4. Bean Lifecycle
    Instantiation → Populate Properties → BeanNameAware →
    BeanFactoryAware → ApplicationContextAware →
    @PostConstruct → InitializingBean.afterPropertiesSet() →
    custom init-method → Bean Ready →
    @PreDestroy → DisposableBean.destroy() → custom destroy-method
    
  5. AOP (Aspect-Oriented Programming)
    • Cross-cutting concerns: logging, security, transactions
    • Advice: Code to execute (before, after, around)
    • Pointcut: Where to apply advice
    • Proxy: Wrapper that intercepts method calls
  6. Spring MVC Request Flow
    HTTP Request → Filter Chain → DispatcherServlet →
    HandlerMapping → HandlerAdapter → Interceptors (preHandle) →
    Controller → Interceptors (postHandle) →
    ViewResolver → View → Response
    

Framework Comparison Overview

Before diving into projects, let’s understand where Spring sits in the ecosystem:

Java Frameworks

Framework Approach Startup Time Memory Best For
Spring Boot Runtime reflection, rich ecosystem ~2-5s ~200-500MB Enterprise apps, microservices
Quarkus Build-time optimization, GraalVM native ~10-50ms ~20-50MB Cloud-native, serverless
Micronaut Compile-time DI, no reflection ~10-50ms ~20-50MB Microservices, IoT
Jakarta EE Standards-based, vendor-neutral ~3-10s ~200-400MB Enterprise, portable apps

Cross-Language Comparison

Framework Language Philosophy Performance Learning Curve
Spring Boot Java Convention over config, comprehensive Good Steep
ASP.NET Core C# Similar to Spring, Microsoft ecosystem Excellent Moderate
Django Python Batteries included, ORM-first Moderate Gentle
Express Node.js Minimal, middleware-based Very Good Easy
Rails Ruby Convention over config, happiness Moderate Moderate
Gin/Echo Go Minimal, explicit, fast Excellent Easy

Project List

Projects are ordered from foundational understanding to advanced implementations.


Project 1: Build Your Own IoC Container (Understand Dependency Injection)

  • File: SPRING_FRAMEWORK_DEEP_DIVE_PROJECTS.md
  • Main Programming Language: Java
  • Alternative Programming Languages: Kotlin, Scala
  • Coolness Level: Level 4: Hardcore Tech Flex
  • Business Potential: 1. The “Resume Gold”
  • Difficulty: Level 3: Advanced
  • Knowledge Area: Reflection / Dependency Injection / Design Patterns
  • Software or Tool: Java Reflection API
  • Main Book: “Pro Spring 6” by Iuliana Cosmina

What you’ll build: A miniature IoC container that scans for annotated classes (@Component, @Autowired), creates instances, resolves dependencies, and injects them—exactly like Spring does, but in ~500 lines of code.

Why it teaches Spring internals: You cannot truly understand Spring until you’ve built a tiny version yourself. This project demystifies the “magic” of @Autowired by implementing it from scratch using Java reflection.

Core challenges you’ll face:

  • Classpath scanning → maps to finding annotated classes at startup
  • Dependency resolution order → maps to topological sorting of bean dependencies
  • Circular dependency detection → maps to preventing infinite loops
  • Singleton vs prototype scope → maps to bean lifecycle management
  • Constructor vs setter injection → maps to different injection strategies

Key Concepts:

Difficulty: Advanced Time estimate: 1-2 weeks Prerequisites: Core Java, understanding of reflection, generics

Real world outcome:

// Your custom annotations
@MyComponent
public class UserService {
    @MyAutowired
    private UserRepository userRepository;

    public User findUser(Long id) {
        return userRepository.findById(id);
    }
}

@MyComponent
public class UserRepository {
    public User findById(Long id) {
        return new User(id, "John Doe");
    }
}

// Your IoC container in action
public class Main {
    public static void main(String[] args) {
        MyApplicationContext context = new MyApplicationContext("com.example");

        // Container scans, instantiates, and wires everything
        System.out.println("Registered beans:");
        context.getBeanNames().forEach(System.out::println);
        // Output:
        // userService
        // userRepository

        UserService userService = context.getBean(UserService.class);
        User user = userService.findUser(1L);
        System.out.println(user.getName()); // "John Doe"
    }
}

Implementation Hints:

The IoC container process:

  1. Scan: Find all classes with @MyComponent annotation
  2. Parse: Extract dependency information (constructor params, @MyAutowired fields)
  3. Sort: Topologically sort to resolve dependencies in correct order
  4. Instantiate: Create instances using reflection
  5. Inject: Set dependencies on each instance
  6. Store: Keep singletons in a registry

Key Java Reflection APIs you’ll use:

  • Class.forName() - Load class by name
  • class.getDeclaredConstructors() - Get constructors
  • class.getDeclaredFields() - Get fields (including private)
  • field.setAccessible(true) - Access private fields
  • field.set(instance, value) - Set field value
  • constructor.newInstance(args) - Create instance

Questions to guide your implementation:

  • How do you find all classes in a package at runtime?
  • What happens when A depends on B, and B depends on A?
  • How do you handle constructor injection with multiple parameters?
  • What if a dependency doesn’t exist?

Learning milestones:

  1. You scan and find annotated classes → You understand classpath scanning
  2. You resolve simple dependencies → You understand basic DI
  3. You detect circular dependencies → You understand why Spring sometimes fails
  4. You implement multiple scopes → You understand bean lifecycle

Project 2: Build Your Own AOP Framework (Understand Proxies)

  • File: SPRING_FRAMEWORK_DEEP_DIVE_PROJECTS.md
  • Main Programming Language: Java
  • Alternative Programming Languages: Kotlin, Scala
  • Coolness Level: Level 4: Hardcore Tech Flex
  • Business Potential: 1. The “Resume Gold”
  • Difficulty: Level 4: Expert
  • Knowledge Area: AOP / Proxies / Bytecode Manipulation
  • Software or Tool: JDK Dynamic Proxy, CGLIB
  • Main Book: “Spring in Action” by Craig Walls

What you’ll build: An AOP framework that creates proxies (both JDK Dynamic and CGLIB), intercepts method calls, and applies advice (before, after, around)—exactly like Spring AOP works.

Why it teaches Spring AOP: @Transactional, @Secured, @Cacheable all work through AOP proxies. Understanding proxy mechanics explains why self-invocation doesn’t trigger aspects and why you can’t advise final methods.

Core challenges you’ll face:

  • JDK Dynamic Proxy creation → maps to interface-based proxying
  • CGLIB proxy creation → maps to subclass-based proxying
  • Method interception → maps to InvocationHandler/MethodInterceptor
  • Pointcut expressions → maps to matching which methods to advise
  • Advice chaining → maps to multiple aspects on one method

Key Concepts:

Difficulty: Expert Time estimate: 2-3 weeks Prerequisites: Project 1 (IoC Container), understanding of interfaces and inheritance

Real world outcome:

// Your custom annotations
@MyAspect
public class LoggingAspect {

    @MyBefore("execution(* UserService.*(..))")
    public void logBefore(MethodInvocation invocation) {
        System.out.println(">>> Calling: " + invocation.getMethod().getName());
    }

    @MyAfter("execution(* UserService.*(..))")
    public void logAfter(MethodInvocation invocation, Object result) {
        System.out.println("<<< Returned: " + result);
    }

    @MyAround("execution(* *.save*(..))")
    public Object measureTime(MethodInvocation invocation) throws Throwable {
        long start = System.currentTimeMillis();
        Object result = invocation.proceed();
        long duration = System.currentTimeMillis() - start;
        System.out.println("Method took: " + duration + "ms");
        return result;
    }
}

// Usage
public class Main {
    public static void main(String[] args) {
        MyAopContext context = new MyAopContext("com.example");

        UserService userService = context.getBean(UserService.class);
        // This is actually a PROXY wrapping the real UserService

        User user = userService.findUser(1L);
        // Output:
        // >>> Calling: findUser
        // <<< Returned: User{id=1, name='John Doe'}

        userService.saveUser(new User(2L, "Jane"));
        // Output:
        // >>> Calling: saveUser
        // Method took: 150ms
        // <<< Returned: User{id=2, name='Jane'}

        // Demonstrate self-invocation limitation
        System.out.println("Proxy class: " + userService.getClass().getName());
        // Output: Proxy class: com.example.UserService$$EnhancerByCGLIB$$abc123
    }
}

Implementation Hints:

Two proxy types:

JDK Dynamic Proxy (for interfaces):

// Pseudo-code structure
Proxy.newProxyInstance(
    classLoader,
    new Class<?>[] { UserService.class },  // Interface
    (proxy, method, args) -> {
        // Before advice
        Object result = method.invoke(target, args);
        // After advice
        return result;
    }
);

CGLIB Proxy (for classes):

// Pseudo-code structure
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(UserService.class);
enhancer.setCallback((MethodInterceptor) (obj, method, args, methodProxy) -> {
    // Before advice
    Object result = methodProxy.invokeSuper(obj, args);
    // After advice
    return result;
});
return enhancer.create();

Why self-invocation bypasses AOP: When method A calls method B internally using this.methodB(), it bypasses the proxy. The call goes directly to the target object, not through the proxy wrapper.

Questions to guide your implementation:

  • When do you use JDK proxy vs CGLIB?
  • How do you chain multiple aspects on one method?
  • How do you parse pointcut expressions like execution(* *.save*(..))?
  • Why can’t you advise final methods with CGLIB?

Learning milestones:

  1. You create JDK dynamic proxies → You understand interface-based proxying
  2. You create CGLIB proxies → You understand subclass-based proxying
  3. You implement around advice → You understand method interception
  4. You chain multiple aspects → You understand advice ordering

Project 3: Build a Mini Spring MVC (Understand DispatcherServlet)

  • File: SPRING_FRAMEWORK_DEEP_DIVE_PROJECTS.md
  • Main Programming Language: Java
  • Alternative Programming Languages: Kotlin
  • Coolness Level: Level 4: Hardcore Tech Flex
  • Business Potential: 1. The “Resume Gold”
  • Difficulty: Level 3: Advanced
  • Knowledge Area: Web Frameworks / Servlet API / MVC Pattern
  • Software or Tool: Servlet API, Embedded Tomcat/Jetty
  • Main Book: “Spring in Action” by Craig Walls

What you’ll build: A mini MVC framework with a central DispatcherServlet, handler mapping (annotations like @GetMapping), argument resolution (@PathVariable, @RequestBody), and JSON response handling.

Why it teaches Spring MVC: Every Spring web request flows through DispatcherServlet. Understanding its pipeline—handler mapping, adapter, interceptors, exception handlers—explains how requests become responses.

Core challenges you’ll face:

  • Servlet integration → maps to HttpServletRequest/Response handling
  • Handler mapping → maps to matching URLs to controller methods
  • Argument resolution → maps to parsing @PathVariable, @RequestParam, @RequestBody
  • Return value handling → maps to converting objects to JSON responses
  • Exception handling → maps to @ExceptionHandler mechanism

Key Concepts:

Difficulty: Advanced Time estimate: 2-3 weeks Prerequisites: Project 1 (IoC Container), Java Servlets basics, HTTP protocol

Real world outcome:

// Your custom annotations
@MyController
@MyRequestMapping("/users")
public class UserController {

    @MyAutowired
    private UserService userService;

    @MyGetMapping("/{id}")
    public User getUser(@MyPathVariable("id") Long id) {
        return userService.findById(id);
    }

    @MyPostMapping
    public User createUser(@MyRequestBody User user) {
        return userService.save(user);
    }

    @MyExceptionHandler(UserNotFoundException.class)
    public ErrorResponse handleNotFound(UserNotFoundException e) {
        return new ErrorResponse(404, e.getMessage());
    }
}

// Starting your framework
public class Main {
    public static void main(String[] args) {
        MySpringMvc app = new MySpringMvc("com.example");
        app.start(8080);

        System.out.println("Server started on http://localhost:8080");
        System.out.println("Registered endpoints:");
        // GET  /users/{id}
        // POST /users
    }
}

// Testing with curl
// $ curl http://localhost:8080/users/1
// {"id":1,"name":"John Doe"}

// $ curl -X POST -H "Content-Type: application/json" \
//        -d '{"name":"Jane"}' http://localhost:8080/users
// {"id":2,"name":"Jane"}

Implementation Hints:

DispatcherServlet pipeline:

Request
   ↓
┌─────────────────────────────────────────┐
│           DispatcherServlet              │
│  1. Find handler (HandlerMapping)        │
│  2. Get adapter (HandlerAdapter)         │
│  3. Run pre-interceptors                 │
│  4. Invoke handler (controller method)   │
│  5. Run post-interceptors                │
│  6. Handle exceptions if any             │
│  7. Render view / write response         │
└─────────────────────────────────────────┘
   ↓
Response

Handler registration:

  1. At startup, scan for @MyController classes
  2. For each method with @MyGetMapping, etc., store:
    • HTTP method (GET, POST, etc.)
    • URL pattern (“/users/{id}”)
    • Method reference
    • Parameter info (names, types, annotations)

Questions to guide your implementation:

  • How do you match “/users/123” to “/users/{id}” and extract “123”?
  • How do you convert the extracted “123” string to Long?
  • How do you deserialize JSON request body to a Java object?
  • How do you serialize the returned object to JSON?

Learning milestones:

  1. You handle basic GET requests → You understand handler mapping
  2. You parse path variables → You understand argument resolution
  3. You handle POST with JSON body → You understand request body handling
  4. You implement exception handling → You understand error pipelines

Project 4: Spring Boot Auto-Configuration Simulator (Understand the Magic)

  • File: SPRING_FRAMEWORK_DEEP_DIVE_PROJECTS.md
  • Main Programming Language: Java
  • Alternative Programming Languages: Kotlin
  • Coolness Level: Level 3: Genuinely Clever
  • Business Potential: 1. The “Resume Gold”
  • Difficulty: Level 3: Advanced
  • Knowledge Area: Configuration / Conditional Logic / SPI
  • Software or Tool: Java ServiceLoader, Annotation Processing
  • Main Book: “Learning Spring Boot 3.0” by Greg Turnquist

What you’ll build: An auto-configuration system that detects classpath dependencies and conditionally registers beans—exactly how Spring Boot’s @ConditionalOnClass, @ConditionalOnProperty work.

Why it teaches Spring Boot: Spring Boot “just works” because of auto-configuration. Understanding the spring.factories/AutoConfiguration.imports mechanism and conditional annotations explains why adding a dependency is often enough.

Core challenges you’ll face:

  • Classpath detection → maps to @ConditionalOnClass
  • Property-based configuration → maps to @ConditionalOnProperty
  • Bean existence checks → maps to @ConditionalOnMissingBean
  • Configuration ordering → maps to @AutoConfigureAfter/@Before
  • Starter creation → maps to packaging auto-configurations

Key Concepts:

Difficulty: Advanced Time estimate: 1-2 weeks Prerequisites: Project 1 (IoC Container), understanding of annotations

Real world outcome:

// Auto-configuration class
@MyAutoConfiguration
@MyConditionalOnClass(name = "com.mysql.cj.jdbc.Driver")
public class MySqlAutoConfiguration {

    @MyBean
    @MyConditionalOnMissingBean(DataSource.class)
    @MyConditionalOnProperty(name = "db.enabled", havingValue = "true")
    public DataSource dataSource(MyEnvironment env) {
        return new MySqlDataSource(
            env.getProperty("db.url"),
            env.getProperty("db.username"),
            env.getProperty("db.password")
        );
    }
}

// Another auto-config that depends on the first
@MyAutoConfiguration
@MyAutoConfigureAfter(MySqlAutoConfiguration.class)
public class JpaAutoConfiguration {

    @MyBean
    @MyConditionalOnBean(DataSource.class)
    public EntityManager entityManager(DataSource ds) {
        return new EntityManager(ds);
    }
}

// Application startup
public class Main {
    public static void main(String[] args) {
        MyAutoConfigContext context = new MyAutoConfigContext("com.example");
        context.setProperty("db.enabled", "true");
        context.setProperty("db.url", "jdbc:mysql://localhost:3306/test");

        context.refresh();

        System.out.println("Auto-configured beans:");
        context.getBeanNames().forEach(name -> {
            Object bean = context.getBean(name);
            System.out.println("  " + name + " -> " + bean.getClass().getSimpleName());
        });
        // Output (if MySQL driver is on classpath):
        //   dataSource -> MySqlDataSource
        //   entityManager -> EntityManager

        // If MySQL driver is NOT on classpath:
        //   (no dataSource or entityManager beans)

        // Show evaluation report
        context.printConditionReport();
        // MySqlAutoConfiguration:
        //   @ConditionalOnClass(com.mysql.cj.jdbc.Driver): MATCH
        // MySqlAutoConfiguration.dataSource:
        //   @ConditionalOnMissingBean(DataSource): MATCH (no existing bean)
        //   @ConditionalOnProperty(db.enabled=true): MATCH
        // JpaAutoConfiguration.entityManager:
        //   @ConditionalOnBean(DataSource): MATCH (dataSource exists)
    }
}

Implementation Hints:

Spring Boot’s auto-configuration discovery:

  1. At startup, read META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
  2. Load each listed class
  3. Evaluate conditions (classpath, properties, existing beans)
  4. Register matching configurations

Condition evaluation order:

  1. Class conditions first (@ConditionalOnClass) - cheapest
  2. Bean conditions (@ConditionalOnBean) - requires context
  3. Property conditions (@ConditionalOnProperty) - environment lookup

How to check if a class is on classpath:

try {
    Class.forName("com.mysql.cj.jdbc.Driver");
    return true; // Class exists
} catch (ClassNotFoundException e) {
    return false; // Class doesn't exist
}

Questions to guide your implementation:

  • Why are class conditions evaluated before bean conditions?
  • How do you handle circular dependencies between auto-configurations?
  • Why is @ConditionalOnMissingBean important for customization?
  • How do you debug which auto-configurations are active?

Learning milestones:

  1. You implement @ConditionalOnClass → You understand classpath detection
  2. You implement @ConditionalOnProperty → You understand environment config
  3. You implement @ConditionalOnMissingBean → You understand user customization
  4. You create a starter → You understand packaging auto-config

Project 5: Transaction Manager (Understand @Transactional)

  • File: SPRING_FRAMEWORK_DEEP_DIVE_PROJECTS.md
  • Main Programming Language: Java
  • Alternative Programming Languages: Kotlin
  • Coolness Level: Level 3: Genuinely Clever
  • Business Potential: 2. The “Micro-SaaS / Pro Tool”
  • Difficulty: Level 3: Advanced
  • Knowledge Area: Transactions / Database / AOP
  • Software or Tool: JDBC, Connection Pooling
  • Main Book: “Pro Spring 6” by Iuliana Cosmina

What you’ll build: A transaction manager that wraps methods with database transactions—begin, commit on success, rollback on exception—exactly like Spring’s @Transactional works using AOP proxies.

Why it teaches @Transactional: @Transactional is the most misunderstood Spring annotation. Understanding how it uses AOP proxies explains self-invocation issues, propagation levels, and isolation levels.

Core challenges you’ll face:

  • ThreadLocal connection management → maps to one connection per transaction
  • Proxy-based interception → maps to @Transactional via AOP
  • Propagation levels → maps to REQUIRED, REQUIRES_NEW, etc.
  • Rollback rules → maps to which exceptions trigger rollback
  • Read-only optimization → maps to readOnly=true behavior

Key Concepts:

Difficulty: Advanced Time estimate: 2 weeks Prerequisites: Project 2 (AOP Framework), JDBC basics, understanding of database transactions

Real world outcome:

// Service using transactions
@MyService
public class BankService {

    @MyAutowired
    private AccountRepository accountRepository;

    @MyTransactional
    public void transfer(Long fromId, Long toId, BigDecimal amount) {
        Account from = accountRepository.findById(fromId);
        Account to = accountRepository.findById(toId);

        from.withdraw(amount);
        to.deposit(amount);

        accountRepository.save(from);
        accountRepository.save(to);
        // If any exception here, ALL changes roll back
    }

    @MyTransactional(propagation = Propagation.REQUIRES_NEW)
    public void audit(String action) {
        // This runs in a NEW transaction
        // Even if outer transaction rolls back, this commits
        auditRepository.save(new AuditLog(action));
    }

    @MyTransactional(readOnly = true)
    public Account getBalance(Long id) {
        // Read-only hint for optimization
        return accountRepository.findById(id);
    }
}

// Demonstration
public class Main {
    public static void main(String[] args) {
        MyTransactionalContext context = new MyTransactionalContext("com.example");

        BankService bankService = context.getBean(BankService.class);

        // Successful transfer
        bankService.transfer(1L, 2L, new BigDecimal("100"));
        // Transaction: BEGIN → UPDATE → UPDATE → COMMIT

        // Failed transfer (insufficient funds)
        try {
            bankService.transfer(1L, 2L, new BigDecimal("1000000"));
        } catch (InsufficientFundsException e) {
            // Transaction: BEGIN → UPDATE → EXCEPTION → ROLLBACK
            System.out.println("Transfer rolled back: " + e.getMessage());
        }

        // Show transaction logs
        context.printTransactionLog();
        // [TX-1] BEGIN
        // [TX-1] UPDATE accounts SET balance=900 WHERE id=1
        // [TX-1] UPDATE accounts SET balance=100 WHERE id=2
        // [TX-1] COMMIT
        // [TX-2] BEGIN
        // [TX-2] UPDATE accounts SET balance=-99100 WHERE id=1
        // [TX-2] ROLLBACK (InsufficientFundsException)
    }
}

Implementation Hints:

Transaction manager components:

  1. ConnectionHolder: ThreadLocal storing current connection
  2. TransactionInterceptor: AOP advice that wraps methods
  3. TransactionDefinition: Propagation, isolation, timeout, readOnly
  4. TransactionStatus: Current transaction state

Propagation behavior:

  • REQUIRED (default): Join existing or create new
  • REQUIRES_NEW: Always create new, suspend existing
  • NESTED: Create savepoint within existing
  • SUPPORTS: Join existing or run non-transactional
  • MANDATORY: Must have existing, throw if none

Why self-invocation bypasses @Transactional:

public class MyService {
    @Transactional
    public void methodA() {
        this.methodB(); // Direct call, bypasses proxy!
    }

    @Transactional(propagation = REQUIRES_NEW)
    public void methodB() {
        // Runs in SAME transaction as methodA, not a new one!
    }
}

Learning milestones:

  1. You manage connections per thread → You understand ThreadLocal
  2. You implement basic commit/rollback → You understand transaction boundaries
  3. You implement propagation levels → You understand nested transactions
  4. You handle self-invocation → You understand proxy limitations

Project 6: Spring Security Filter Chain (Understand Authentication)

  • File: SPRING_FRAMEWORK_DEEP_DIVE_PROJECTS.md
  • Main Programming Language: Java
  • Alternative Programming Languages: Kotlin
  • Coolness Level: Level 4: Hardcore Tech Flex
  • Business Potential: 3. The “Service & Support” Model
  • Difficulty: Level 4: Expert
  • Knowledge Area: Security / Authentication / Authorization
  • Software or Tool: Servlet Filters, JWT
  • Main Book: “Spring Security in Action” by Laurentiu Spilca

What you’ll build: A security framework with a filter chain that handles authentication (username/password, JWT), authorization (roles, permissions), and security context propagation—exactly like Spring Security works.

Why it teaches Spring Security: Spring Security’s filter chain is notoriously complex. Understanding how SecurityContextHolder, AuthenticationManager, and filter ordering work demystifies security configuration.

Core challenges you’ll face:

  • Filter chain ordering → maps to which filter runs when
  • Authentication flow → maps to AuthenticationManager, Providers
  • Security context storage → maps to SecurityContextHolder (ThreadLocal)
  • Authorization decisions → maps to AccessDecisionManager, voters
  • JWT token handling → maps to stateless authentication

Key Concepts:

Difficulty: Expert Time estimate: 3-4 weeks Prerequisites: Project 3 (Mini Spring MVC), understanding of HTTP, JWT basics

Real world outcome:

// Security configuration
@MySecurityConfig
public class SecurityConfig {

    @MyBean
    public MySecurityFilterChain filterChain(MyHttpSecurity http) {
        return http
            .csrf().disable()
            .authorizeRequests()
                .antMatchers("/public/**").permitAll()
                .antMatchers("/admin/**").hasRole("ADMIN")
                .antMatchers("/api/**").authenticated()
            .and()
            .formLogin()
                .loginPage("/login")
                .successHandler(this::onSuccess)
            .and()
            .jwt()
                .tokenValiditySeconds(3600)
            .and()
            .build();
    }

    @MyBean
    public MyUserDetailsService userDetailsService() {
        return username -> {
            // Load user from database
            return new MyUser("admin", passwordEncoder.encode("secret"),
                              Set.of("ROLE_ADMIN", "ROLE_USER"));
        };
    }
}

// Protected controller
@MyController
public class AdminController {

    @MyGetMapping("/admin/dashboard")
    @MyPreAuthorize("hasRole('ADMIN')")
    public String dashboard() {
        MyAuthentication auth = MySecurityContext.getAuthentication();
        return "Welcome, " + auth.getName();
    }
}

// Testing
public class Main {
    public static void main(String[] args) {
        MySecurityApp app = new MySecurityApp("com.example");
        app.start(8080);

        // Unauthenticated request to protected resource
        // curl http://localhost:8080/admin/dashboard
        // Response: 401 Unauthorized

        // Login
        // curl -X POST -d "username=admin&password=secret" \
        //      http://localhost:8080/login
        // Response: {"token": "eyJhbGciOiJIUzI1NiIs..."}

        // Authenticated request
        // curl -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..." \
        //      http://localhost:8080/admin/dashboard
        // Response: "Welcome, admin"

        // Print filter chain
        app.printFilterChain();
        // 1. SecurityContextPersistenceFilter
        // 2. UsernamePasswordAuthenticationFilter
        // 3. JwtAuthenticationFilter
        // 4. ExceptionTranslationFilter
        // 5. FilterSecurityInterceptor (authorization)
    }
}

Implementation Hints:

Spring Security filter chain (simplified):

Request
   ↓
SecurityContextPersistenceFilter  ← Load/save SecurityContext
   ↓
UsernamePasswordAuthenticationFilter  ← Handle login form
   ↓
JwtAuthenticationFilter  ← Handle JWT tokens
   ↓
ExceptionTranslationFilter  ← Convert security exceptions
   ↓
FilterSecurityInterceptor  ← Authorization decisions
   ↓
Your Controller

Authentication flow:

  1. Filter extracts credentials (username/password or JWT)
  2. Create Authentication token (unauthenticated)
  3. Pass to AuthenticationManager
  4. Manager delegates to AuthenticationProviders
  5. Provider validates and returns authenticated token
  6. Store in SecurityContextHolder

SecurityContextHolder uses ThreadLocal:

// Storing authentication
SecurityContextHolder.getContext().setAuthentication(auth);

// Retrieving anywhere in the thread
Authentication auth = SecurityContextHolder.getContext().getAuthentication();

Learning milestones:

  1. You implement filter chain → You understand request processing
  2. You handle username/password → You understand form login
  3. You implement JWT → You understand stateless auth
  4. You implement role-based access → You understand authorization

Project 7: Spring Data Repository Magic (Understand Query Generation)

  • File: SPRING_FRAMEWORK_DEEP_DIVE_PROJECTS.md
  • Main Programming Language: Java
  • Alternative Programming Languages: Kotlin
  • Coolness Level: Level 3: Genuinely Clever
  • Business Potential: 2. The “Micro-SaaS / Pro Tool”
  • Difficulty: Level 3: Advanced
  • Knowledge Area: ORM / Query Generation / Proxies
  • Software or Tool: JDBC, Proxy Generation
  • Main Book: “Spring Data” by Mark Pollack et al.

What you’ll build: A repository framework that generates query implementations from method names—findByNameAndAge, deleteByStatus—exactly like Spring Data JPA works using dynamic proxies.

Why it teaches Spring Data: Spring Data’s magic comes from parsing method names into queries at runtime. Understanding this mechanism explains the naming conventions and limitations.

Core challenges you’ll face:

  • Method name parsing → maps to findBy, deleteBy, countBy conventions
  • Query generation → maps to building SQL from parsed method name
  • Dynamic proxy creation → maps to interface-only repositories
  • Pagination and sorting → maps to Pageable, Sort parameters
  • Custom queries → maps to @Query annotation support

Key Concepts:

Difficulty: Advanced Time estimate: 2-3 weeks Prerequisites: Project 2 (AOP Framework), JDBC basics, understanding of SQL

Real world outcome:

// Your repository interface (no implementation needed!)
@MyRepository
public interface UserRepository extends MyCrudRepository<User, Long> {

    // Automatically generates: SELECT * FROM users WHERE name = ?
    List<User> findByName(String name);

    // Generates: SELECT * FROM users WHERE name = ? AND age > ?
    List<User> findByNameAndAgeGreaterThan(String name, int age);

    // Generates: SELECT * FROM users WHERE email LIKE ?
    Optional<User> findByEmailContaining(String email);

    // Generates: DELETE FROM users WHERE status = ?
    int deleteByStatus(String status);

    // Generates: SELECT COUNT(*) FROM users WHERE active = ?
    long countByActive(boolean active);

    // Pagination: SELECT * FROM users ORDER BY name LIMIT ? OFFSET ?
    MyPage<User> findAll(MyPageable pageable);

    // Custom query
    @MyQuery("SELECT * FROM users WHERE created_at > :date")
    List<User> findRecentUsers(@MyParam("date") LocalDate date);
}

// Usage - the proxy handles everything!
public class Main {
    public static void main(String[] args) {
        MyDataContext context = new MyDataContext("com.example", dataSource);

        UserRepository userRepo = context.getRepository(UserRepository.class);
        // This is a PROXY that generates SQL from method names!

        // Find by name
        List<User> johns = userRepo.findByName("John");
        // Executed: SELECT * FROM users WHERE name = 'John'

        // Complex query
        List<User> adults = userRepo.findByNameAndAgeGreaterThan("John", 18);
        // Executed: SELECT * FROM users WHERE name = 'John' AND age > 18

        // Pagination
        MyPage<User> page = userRepo.findAll(MyPageable.of(0, 10, MySort.by("name")));
        // Executed: SELECT * FROM users ORDER BY name LIMIT 10 OFFSET 0

        // Show query generation log
        context.printQueryLog();
    }
}

Implementation Hints:

Method name parsing rules:

findByNameAndAgeGreaterThan(String name, int age)
     │    │   │             │
     │    │   │             └── GreaterThan → age > ?
     │    │   └── And → AND operator
     │    └── Name → WHERE name = ?
     └── findBy → SELECT query

Supported keywords:

  • And, Or → Boolean operators
  • Is, Equals → Equality (default)
  • Between → BETWEEN x AND y
  • LessThan, GreaterThan → Comparison
  • Like, Containing, StartingWith, EndingWith → LIKE patterns
  • OrderBy → ORDER BY clause
  • Not, IsNull, IsNotNull → Negation and nulls

Proxy implementation: The repository is an interface with no implementation. At runtime:

  1. Create a JDK dynamic proxy for the interface
  2. When a method is called, parse its name
  3. Generate SQL from the parsed tokens
  4. Execute SQL and map results to objects

Learning milestones:

  1. You parse method names → You understand the convention
  2. You generate simple queries → You understand query derivation
  3. You handle pagination → You understand LIMIT/OFFSET
  4. You implement custom queries → You understand @Query

Project 8: Build a Quarkus-Style Framework (Understand Compile-Time DI)

  • File: SPRING_FRAMEWORK_DEEP_DIVE_PROJECTS.md
  • Main Programming Language: Java
  • Alternative Programming Languages: Kotlin
  • Coolness Level: Level 4: Hardcore Tech Flex
  • Business Potential: 4. The “Open Core” Infrastructure
  • Difficulty: Level 4: Expert
  • Knowledge Area: Annotation Processing / Compile-Time DI / AOT
  • Software or Tool: Java Annotation Processor, GraalVM Native Image
  • Main Book: “Quarkus Cookbook” by Alex Soto Bueno & Jason Porter

What you’ll build: A framework that does dependency injection at compile time (not runtime reflection)—generating wiring code during compilation, resulting in instant startup and GraalVM native image compatibility.

Why it teaches alternative approaches: Spring’s runtime reflection is elegant but slow to start. Understanding how Quarkus/Micronaut avoid reflection explains the performance difference and tradeoffs.

Core challenges you’ll face:

  • Annotation processing → maps to generating code at compile time
  • Dependency graph building → maps to determining injection order
  • Code generation → maps to creating actual Java source files
  • Native image compatibility → maps to no runtime reflection
  • Startup optimization → maps to moving work from runtime to build time

Key Concepts:

Difficulty: Expert Time estimate: 3-4 weeks Prerequisites: Projects 1-2, understanding of annotation processors, build tools

Real world outcome:

// Your annotations (same as Spring!)
@MyComponent
public class UserService {
    @MyInject
    private UserRepository userRepository;

    public User findUser(Long id) {
        return userRepository.findById(id);
    }
}

// At COMPILE TIME, your annotation processor generates:
// (This file is auto-generated in target/generated-sources/)
public class UserService_BeanFactory implements BeanFactory<UserService> {

    private final UserRepository userRepository;

    public UserService_BeanFactory(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    @Override
    public UserService create() {
        UserService instance = new UserService();
        instance.userRepository = this.userRepository;  // Direct field access
        return instance;
    }
}

// Generated context initialization (no reflection!)
public class GeneratedBeanContext {
    public void initialize() {
        // Direct instantiation - no Class.forName(), no reflection
        UserRepository userRepository = new UserRepository();
        UserService_BeanFactory userServiceFactory = new UserService_BeanFactory(userRepository);
        UserService userService = userServiceFactory.create();

        registerBean(UserRepository.class, userRepository);
        registerBean(UserService.class, userService);
    }
}

// Application startup - INSTANT!
public class Main {
    public static void main(String[] args) {
        long start = System.nanoTime();

        MyAotContext context = new MyAotContext();

        long startup = (System.nanoTime() - start) / 1_000_000;
        System.out.println("Started in " + startup + "ms");
        // Output: Started in 3ms (vs Spring's ~2000ms!)

        UserService userService = context.getBean(UserService.class);
        System.out.println(userService.findUser(1L));
    }
}

Implementation Hints:

Annotation Processor structure:

@SupportedAnnotationTypes("com.example.MyComponent")
@SupportedSourceVersion(SourceVersion.RELEASE_17)
public class MyComponentProcessor extends AbstractProcessor {

    @Override
    public boolean process(Set<? extends TypeElement> annotations,
                          RoundEnvironment roundEnv) {
        // 1. Find all @MyComponent classes
        // 2. Analyze their dependencies (@MyInject fields)
        // 3. Generate factory classes
        // 4. Generate context initialization code

        for (Element element : roundEnv.getElementsAnnotatedWith(MyComponent.class)) {
            // Generate factory class
            generateBeanFactory((TypeElement) element);
        }
        return true;
    }

    private void generateBeanFactory(TypeElement classElement) {
        // Use JavaPoet or Filer to write Java source files
        JavaFileObject file = processingEnv.getFiler()
            .createSourceFile(classElement.getSimpleName() + "_BeanFactory");
        // Write generated code...
    }
}

Why compile-time DI is faster:

  • No classpath scanning at runtime
  • No reflection to create instances
  • No proxy generation (CGLIB)
  • Dependency graph known at compile time
  • Dead code elimination possible

Tradeoffs:

  • Less dynamic (can’t change wiring at runtime)
  • Longer compile times
  • Need to recompile for configuration changes

Learning milestones:

  1. You write an annotation processor → You understand compile-time code generation
  2. You generate factory classes → You understand reflection-free DI
  3. You build a native image → You understand GraalVM compatibility
  4. You compare startup times → You understand the performance difference

Project 9: Cross-Framework Comparison App (Same App, 5 Frameworks)

  • File: SPRING_FRAMEWORK_DEEP_DIVE_PROJECTS.md
  • Main Programming Language: Multiple
  • Alternative Programming Languages: N/A (this IS the multi-language project)
  • Coolness Level: Level 3: Genuinely Clever
  • Business Potential: 1. The “Resume Gold”
  • Difficulty: Level 3: Advanced
  • Knowledge Area: Web Frameworks / Comparative Analysis
  • Software or Tool: Spring Boot, Quarkus, Django, Express, ASP.NET Core
  • Main Book: Multiple framework books

What you’ll build: The exact same REST API implemented in 5 different frameworks—Spring Boot, Quarkus, Django, Express, and ASP.NET Core—with benchmarks comparing startup time, memory, throughput, and developer experience.

Why this teaches framework comparison: Theory only goes so far. Building the same app in multiple frameworks reveals real differences in philosophy, patterns, and performance.

Core challenges you’ll face:

  • Idiomatic code per framework → maps to understanding each framework’s conventions
  • Equivalent features → maps to finding analogous patterns
  • Fair benchmarking → maps to controlling variables
  • Docker containerization → maps to consistent deployment
  • Objective comparison → maps to avoiding bias

Key Concepts:

Difficulty: Advanced Time estimate: 3-4 weeks Prerequisites: Basic knowledge of Java, Python, JavaScript, C#

Real world outcome:

project-comparison/
├── spring-boot-app/
│   ├── src/main/java/com/example/
│   │   ├── UserController.java
│   │   ├── UserService.java
│   │   ├── UserRepository.java
│   │   └── User.java
│   ├── pom.xml
│   └── Dockerfile
│
├── quarkus-app/
│   ├── src/main/java/com/example/
│   │   ├── UserResource.java
│   │   ├── UserService.java
│   │   └── User.java
│   ├── pom.xml
│   └── Dockerfile
│
├── django-app/
│   ├── users/
│   │   ├── views.py
│   │   ├── models.py
│   │   ├── serializers.py
│   │   └── urls.py
│   ├── requirements.txt
│   └── Dockerfile
│
├── express-app/
│   ├── src/
│   │   ├── routes/users.js
│   │   ├── services/userService.js
│   │   └── models/user.js
│   ├── package.json
│   └── Dockerfile
│
├── aspnet-app/
│   ├── Controllers/UsersController.cs
│   ├── Services/UserService.cs
│   ├── Models/User.cs
│   ├── Program.cs
│   └── Dockerfile
│
├── docker-compose.yml
├── benchmark.sh
└── RESULTS.md

Benchmark Results Template:

# Framework Comparison Results

## API Specification
- GET /users - List all users
- GET /users/{id} - Get user by ID
- POST /users - Create user
- PUT /users/{id} - Update user
- DELETE /users/{id} - Delete user

## Startup Time (cold start)
| Framework      | JVM/Runtime | Native/AOT |
|---------------|-------------|------------|
| Spring Boot   | 2,450ms     | 45ms       |
| Quarkus       | 890ms       | 12ms       |
| Django        | 1,200ms     | N/A        |
| Express       | 180ms       | N/A        |
| ASP.NET Core  | 650ms       | 28ms       |

## Memory Usage (idle)
| Framework      | JVM/Runtime | Native/AOT |
|---------------|-------------|------------|
| Spring Boot   | 285MB       | 45MB       |
| Quarkus       | 95MB        | 22MB       |
| Django        | 65MB        | N/A        |
| Express       | 45MB        | N/A        |
| ASP.NET Core  | 85MB        | 35MB       |

## Throughput (requests/second)
| Framework      | Simple GET | Complex Query |
|---------------|------------|---------------|
| Spring Boot   | 35,000     | 12,000       |
| Quarkus       | 42,000     | 15,000       |
| Django        | 8,000      | 3,500        |
| Express       | 28,000     | 9,000        |
| ASP.NET Core  | 50,000     | 18,000       |

## Developer Experience
| Aspect           | Spring | Quarkus | Django | Express | ASP.NET |
|-----------------|--------|---------|--------|---------|---------|
| Learning curve  | Steep  | Medium  | Gentle | Easy    | Medium  |
| Documentation   | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐  | ⭐⭐⭐⭐⭐ | ⭐⭐⭐   | ⭐⭐⭐⭐  |
| Ecosystem       | ⭐⭐⭐⭐⭐ | ⭐⭐⭐   | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐  |
| Hot reload      | ⭐⭐⭐   | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐  | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐  |
| IDE support     | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐  | ⭐⭐⭐⭐  | ⭐⭐⭐   | ⭐⭐⭐⭐⭐ |

Implementation Hints:

Each implementation should have:

  1. REST endpoints (CRUD for User entity)
  2. Service layer with business logic
  3. Repository/data access layer
  4. Input validation
  5. Error handling
  6. JSON serialization
  7. Basic authentication
  8. Database connection (PostgreSQL)

Fair benchmarking tips:

  • Use the same database (PostgreSQL)
  • Same hardware (Docker with resource limits)
  • Same data set
  • Warm-up runs before measuring
  • Multiple runs for statistical significance

Comparison dimensions:

  • Performance (startup, memory, throughput)
  • Developer experience (learning curve, tooling)
  • Ecosystem (libraries, community, jobs)
  • Philosophy (convention vs. configuration)

Learning milestones:

  1. You implement in all frameworks → You understand each framework’s patterns
  2. You benchmark consistently → You understand fair comparison
  3. You document tradeoffs → You can make informed recommendations
  4. You present results objectively → You avoid framework tribalism

Project 10: Spring Boot Application Debugger (Understand Internals)

  • File: SPRING_FRAMEWORK_DEEP_DIVE_PROJECTS.md
  • Main Programming Language: Java
  • Alternative Programming Languages: Kotlin
  • Coolness Level: Level 3: Genuinely Clever
  • Business Potential: 2. The “Micro-SaaS / Pro Tool”
  • Difficulty: Level 2: Intermediate
  • Knowledge Area: Debugging / Observability / Spring Internals
  • Software or Tool: Spring Boot Actuator, Micrometer
  • Main Book: “Spring Boot in Practice” by Somnath Musib

What you’ll build: A debugging/introspection tool that visualizes a running Spring Boot application—bean graph, request flow, transaction boundaries, auto-configuration decisions—giving you X-ray vision into the framework.

Why it teaches debugging: When Spring apps misbehave, you need to understand what’s happening. This project teaches you to leverage Actuator, debugging endpoints, and internal Spring APIs to diagnose issues.

Core challenges you’ll face:

  • Bean graph visualization → maps to understanding dependencies
  • Request tracing → maps to following request flow
  • Auto-config report → maps to understanding what’s active
  • Transaction monitoring → maps to seeing transaction boundaries
  • Memory analysis → maps to detecting bean bloat

Key Concepts:

Difficulty: Intermediate Time estimate: 1-2 weeks Prerequisites: Basic Spring Boot application, understanding of REST APIs

Real world outcome:

# Start your Spring Boot app with the debugger enabled

$ curl http://localhost:8080/debugger/beans
{
  "total": 245,
  "byType": {
    "CONTROLLER": 12,
    "SERVICE": 23,
    "REPOSITORY": 8,
    "CONFIGURATION": 45,
    "INFRASTRUCTURE": 157
  },
  "graph": {
    "userController": ["userService", "securityService"],
    "userService": ["userRepository", "emailService"],
    "userRepository": ["dataSource", "entityManager"]
  }
}

$ curl http://localhost:8080/debugger/beans/userService
{
  "name": "userService",
  "type": "com.example.UserService",
  "scope": "singleton",
  "dependsOn": ["userRepository", "emailService"],
  "usedBy": ["userController", "adminController"],
  "proxy": true,
  "proxyType": "CGLIB",
  "aspects": ["@Transactional", "@Cacheable"],
  "created": "2024-01-15T10:23:45Z"
}

$ curl http://localhost:8080/debugger/autoconfig
{
  "matched": [
    {
      "class": "DataSourceAutoConfiguration",
      "conditions": [
        { "condition": "@ConditionalOnClass(DataSource.class)", "matched": true },
        { "condition": "@ConditionalOnProperty(spring.datasource.url)", "matched": true }
      ]
    }
  ],
  "notMatched": [
    {
      "class": "MongoAutoConfiguration",
      "conditions": [
        { "condition": "@ConditionalOnClass(MongoClient.class)", "matched": false,
          "reason": "Class not found on classpath" }
      ]
    }
  ]
}

$ curl http://localhost:8080/debugger/request-trace/last
{
  "request": "GET /users/123",
  "timestamp": "2024-01-15T10:25:00Z",
  "duration": "45ms",
  "trace": [
    { "component": "SecurityFilter", "duration": "2ms", "details": "User authenticated" },
    { "component": "DispatcherServlet", "duration": "1ms", "details": "Mapped to UserController.getUser" },
    { "component": "UserController.getUser", "duration": "38ms", "details": "Transaction started" },
    { "component": "UserService.findById", "duration": "35ms", "details": "Cache MISS" },
    { "component": "UserRepository.findById", "duration": "30ms", "details": "SQL executed" },
    { "component": "TransactionInterceptor", "duration": "1ms", "details": "Transaction committed" }
  ]
}

Implementation Hints:

Access Spring internals:

@Component
public class BeanIntrospector {

    @Autowired
    private ApplicationContext context;

    @Autowired
    private ConfigurableListableBeanFactory beanFactory;

    public BeanInfo getBeanInfo(String beanName) {
        Object bean = context.getBean(beanName);
        BeanDefinition definition = beanFactory.getBeanDefinition(beanName);

        return new BeanInfo(
            beanName,
            bean.getClass().getName(),
            definition.getScope(),
            definition.getDependsOn(),
            isProxy(bean),
            getProxyType(bean)
        );
    }

    private boolean isProxy(Object bean) {
        return AopUtils.isAopProxy(bean);
    }
}

Useful Spring Boot Actuator endpoints:

  • /actuator/beans - All beans
  • /actuator/conditions - Auto-configuration report
  • /actuator/mappings - Request mappings
  • /actuator/env - Environment properties
  • /actuator/health - Health checks

Learning milestones:

  1. You visualize bean dependencies → You understand the application structure
  2. You trace request flow → You understand the MVC pipeline
  3. You analyze auto-configuration → You understand why beans exist
  4. You monitor transactions → You understand @Transactional behavior

Project 11: Reactive Spring WebFlux (Understand Non-Blocking)

  • File: SPRING_FRAMEWORK_DEEP_DIVE_PROJECTS.md
  • Main Programming Language: Java
  • Alternative Programming Languages: Kotlin (with coroutines)
  • Coolness Level: Level 4: Hardcore Tech Flex
  • Business Potential: 3. The “Service & Support” Model
  • Difficulty: Level 4: Expert
  • Knowledge Area: Reactive Programming / Non-Blocking I/O
  • Software or Tool: Spring WebFlux, Project Reactor
  • Main Book: “Hands-On Reactive Programming in Spring 5” by Oleh Dokuka & Igor Lozynskyi

What you’ll build: A high-concurrency application using Spring WebFlux with reactive streams, non-blocking database access (R2DBC), and reactive security—understanding how it differs from traditional Spring MVC.

Why it teaches reactive Spring: Reactive programming is fundamentally different from imperative. Understanding Mono/Flux, backpressure, and the reactive pipeline explains when and why to use WebFlux.

Core challenges you’ll face:

  • Reactive types (Mono/Flux) → maps to understanding publishers and subscribers
  • Non-blocking database access → maps to R2DBC vs JDBC
  • Backpressure handling → maps to controlling data flow
  • Testing reactive streams → maps to StepVerifier usage
  • Debugging reactive code → maps to stack traces and hooks

Key Concepts:

Difficulty: Expert Time estimate: 3-4 weeks Prerequisites: Projects 1-5, understanding of async programming, Java 8+ streams

Real world outcome:

// Reactive controller
@RestController
@RequestMapping("/users")
public class UserController {

    @Autowired
    private UserService userService;

    @GetMapping("/{id}")
    public Mono<User> getUser(@PathVariable Long id) {
        return userService.findById(id);
    }

    @GetMapping
    public Flux<User> getAllUsers() {
        return userService.findAll();
    }

    @GetMapping("/stream")
    @Produces(MediaType.TEXT_EVENT_STREAM_VALUE)
    public Flux<User> streamUsers() {
        return userService.findAll()
            .delayElements(Duration.ofSeconds(1));  // SSE every second
    }
}

// Reactive service with composition
@Service
public class UserService {

    @Autowired
    private UserRepository userRepository;  // R2DBC

    @Autowired
    private WebClient emailServiceClient;

    public Mono<User> createUserWithNotification(User user) {
        return userRepository.save(user)
            .flatMap(savedUser ->
                emailServiceClient
                    .post()
                    .uri("/notifications")
                    .bodyValue(new WelcomeEmail(savedUser))
                    .retrieve()
                    .bodyToMono(Void.class)
                    .thenReturn(savedUser)
            )
            .onErrorResume(e -> {
                log.error("Failed to send notification", e);
                return Mono.just(user);  // Return user anyway
            });
    }
}

// Performance comparison
public class Main {
    public static void main(String[] args) {
        // Simulate 10,000 concurrent requests

        // Spring MVC (blocking):
        // - Thread pool: 200 threads
        // - Requests waiting in queue
        // - Memory: 500MB (thread stacks)
        // - Throughput: 2,000 req/sec

        // Spring WebFlux (non-blocking):
        // - Event loop: 4 threads (CPU cores)
        // - All requests handled concurrently
        // - Memory: 50MB
        // - Throughput: 20,000 req/sec
    }
}

Implementation Hints:

Reactive vs Imperative:

// Imperative (Spring MVC)
public User getUser(Long id) {
    User user = userRepository.findById(id);  // Blocks thread!
    return user;
}

// Reactive (Spring WebFlux)
public Mono<User> getUser(Long id) {
    return userRepository.findById(id);  // Returns immediately, executes later
}

Composing reactive streams:

// Sequential operations
Mono<User> result = userRepository.findById(id)
    .flatMap(user -> enrichWithProfile(user))
    .flatMap(user -> enrichWithPreferences(user));

// Parallel operations
Mono<EnrichedUser> result = Mono.zip(
    userRepository.findById(id),
    profileService.getProfile(id),
    preferencesService.getPreferences(id)
).map(tuple -> new EnrichedUser(tuple.getT1(), tuple.getT2(), tuple.getT3()));

Common mistakes:

  • Calling .block() (defeats the purpose)
  • Mixing blocking and non-blocking code
  • Not understanding that nothing happens until you subscribe
  • Ignoring backpressure

Learning milestones:

  1. You use Mono and Flux correctly → You understand reactive types
  2. You compose reactive chains → You understand operators
  3. You handle errors reactively → You understand error propagation
  4. You benchmark against MVC → You understand when WebFlux helps

Final Project: Enterprise Application Platform (Combine Everything)

  • File: SPRING_FRAMEWORK_DEEP_DIVE_PROJECTS.md
  • Main Programming Language: Java
  • Alternative Programming Languages: Kotlin
  • Coolness Level: Level 5: Pure Magic (Super Cool)
  • Business Potential: 5. The “Industry Disruptor”
  • Difficulty: Level 5: Master
  • Knowledge Area: Enterprise Architecture / Full-Stack Spring
  • Software or Tool: Spring Boot, Spring Cloud, Kubernetes
  • Main Book: “Cloud Native Java” by Josh Long & Kenny Bastani

What you’ll build: A complete enterprise application platform with microservices, service discovery, configuration management, API gateway, security (OAuth2/OIDC), distributed tracing, and deployment to Kubernetes—demonstrating mastery of the entire Spring ecosystem.

Why this is the ultimate project: This project proves you understand not just Spring internals, but how to architect production systems. It combines every concept from the previous projects into a cohesive whole.

Core challenges you’ll face:

  • Microservices architecture → maps to service boundaries and communication
  • Service discovery → maps to Eureka/Consul integration
  • Configuration management → maps to Spring Cloud Config
  • API Gateway → maps to Spring Cloud Gateway routing
  • Distributed tracing → maps to Sleuth/Micrometer + Zipkin
  • Container orchestration → maps to Kubernetes deployment

Key Concepts:

Difficulty: Master Time estimate: 2-3 months Prerequisites: All previous projects, Docker, Kubernetes basics

Real world outcome:

enterprise-platform/
├── services/
│   ├── user-service/        # User management
│   ├── order-service/       # Order processing
│   ├── product-service/     # Product catalog
│   ├── notification-service/ # Email/SMS
│   └── payment-service/     # Payment processing
│
├── infrastructure/
│   ├── config-server/       # Spring Cloud Config
│   ├── discovery-server/    # Eureka
│   ├── gateway/             # Spring Cloud Gateway
│   └── auth-server/         # OAuth2/OIDC
│
├── observability/
│   ├── zipkin/             # Distributed tracing
│   ├── prometheus/         # Metrics
│   └── grafana/            # Dashboards
│
├── k8s/
│   ├── namespaces/
│   ├── deployments/
│   ├── services/
│   └── ingress/
│
└── docker-compose.yml      # Local development

Architecture Diagram:

                    ┌───────────────────────────────────────────────────┐
                    │                   Kubernetes Cluster               │
                    ├───────────────────────────────────────────────────┤
                    │                                                   │
     Internet ──────┤     ┌─────────────┐                              │
                    │     │   Ingress   │                              │
                    │     └──────┬──────┘                              │
                    │            │                                      │
                    │     ┌──────▼──────┐                              │
                    │     │   Gateway   │◄──── OAuth2 Integration       │
                    │     │  (Routing)  │                              │
                    │     └──────┬──────┘                              │
                    │            │                                      │
          ┌─────────┼────────────┼────────────┐                        │
          │         │            │            │                        │
   ┌──────▼───┐ ┌───▼────┐ ┌────▼───┐ ┌──────▼─────┐                  │
   │  User    │ │ Order  │ │Product │ │Notification│                  │
   │ Service  │ │Service │ │Service │ │  Service   │                  │
   └────┬─────┘ └───┬────┘ └────┬───┘ └──────┬─────┘                  │
        │           │           │            │                         │
        └───────────┴───────────┴────────────┘                         │
                    │                                                   │
          ┌─────────┼─────────┐                                        │
          │         │         │                                        │
   ┌──────▼───┐ ┌───▼────┐ ┌──▼────┐                                  │
   │ Postgres │ │  Redis │ │Kafka  │                                  │
   └──────────┘ └────────┘ └───────┘                                  │
                    │                                                   │
                    │  ┌─────────────────────────────────────┐        │
                    │  │         Observability Stack          │        │
                    │  │  Prometheus │ Zipkin │ Grafana      │        │
                    │  └─────────────────────────────────────┘        │
                    └───────────────────────────────────────────────────┘

Implementation Hints:

Spring Cloud components:

  1. Config Server: Centralized configuration (Git-backed)
  2. Eureka: Service discovery (services register themselves)
  3. Gateway: API gateway with routing and filters
  4. Sleuth: Distributed tracing (correlation IDs)
  5. OpenFeign: Declarative REST clients

Key patterns to implement:

  • Circuit Breaker: Resilience4j for fault tolerance
  • Event Sourcing: Kafka for async communication
  • CQRS: Separate read/write models
  • Saga: Distributed transactions

Kubernetes deployment considerations:

  • ConfigMaps for Spring configuration
  • Secrets for sensitive data
  • Service mesh (Istio) for traffic management
  • Horizontal Pod Autoscaler for scaling

Learning milestones:

  1. Services communicate via discovery → You understand microservices
  2. Configuration is centralized → You understand 12-factor apps
  3. Traces span services → You understand observability
  4. Platform runs on Kubernetes → You understand cloud-native

Project Comparison Table

Project Difficulty Time Depth of Understanding Fun Factor
1. Build Your Own IoC Container Advanced 1-2 weeks ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐
2. Build Your Own AOP Framework Expert 2-3 weeks ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐
3. Build a Mini Spring MVC Advanced 2-3 weeks ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐⭐
4. Auto-Configuration Simulator Advanced 1-2 weeks ⭐⭐⭐⭐ ⭐⭐⭐
5. Transaction Manager Advanced 2 weeks ⭐⭐⭐⭐⭐ ⭐⭐⭐
6. Security Filter Chain Expert 3-4 weeks ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐
7. Spring Data Repository Magic Advanced 2-3 weeks ⭐⭐⭐⭐ ⭐⭐⭐⭐
8. Quarkus-Style Framework Expert 3-4 weeks ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐⭐
9. Cross-Framework Comparison Advanced 3-4 weeks ⭐⭐⭐⭐ ⭐⭐⭐⭐⭐
10. Spring Boot Debugger Intermediate 1-2 weeks ⭐⭐⭐ ⭐⭐⭐
11. Reactive Spring WebFlux Expert 3-4 weeks ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐
Final: Enterprise Platform Master 2-3 months ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐⭐

For Java Developers New to Spring

  1. Project 10 (Debugger) - See Spring from the outside first
  2. Project 1 (IoC Container) - Understand DI fundamentals
  3. Project 3 (Mini MVC) - Understand web layer
  4. Project 4 (Auto-Configuration) - Understand Spring Boot magic

For Spring Developers Wanting Deeper Understanding

  1. Project 1 (IoC Container) - Demystify @Autowired
  2. Project 2 (AOP Framework) - Demystify @Transactional, @Cacheable
  3. Project 5 (Transaction Manager) - Understand transaction boundaries
  4. Project 6 (Security Filter Chain) - Understand Spring Security

For Architects Comparing Frameworks

  1. Project 9 (Cross-Framework) - Build same app in 5 frameworks
  2. Project 8 (Compile-Time DI) - Understand Quarkus/Micronaut approach
  3. Project 11 (WebFlux) - Understand reactive vs imperative
  4. Final Project - Prove enterprise architecture skills

Summary

# Project Name Main Language
1 Build Your Own IoC Container Java
2 Build Your Own AOP Framework Java
3 Build a Mini Spring MVC Java
4 Spring Boot Auto-Configuration Simulator Java
5 Transaction Manager Java
6 Spring Security Filter Chain Java
7 Spring Data Repository Magic Java
8 Build a Quarkus-Style Framework Java
9 Cross-Framework Comparison App Multiple
10 Spring Boot Application Debugger Java
11 Reactive Spring WebFlux Java
Final Enterprise Application Platform Java

Essential Resources

Official Documentation

Books

  • “Pro Spring 6” by Iuliana Cosmina - Comprehensive Spring reference
  • “Spring in Action” by Craig Walls - Practical Spring guide
  • “Spring Boot in Practice” by Somnath Musib - Real-world patterns
  • “Spring Security in Action” by Laurentiu Spilca - Deep security dive
  • “Cloud Native Java” by Josh Long & Kenny Bastani - Microservices with Spring
  • “Hands-On Reactive Programming in Spring 5” by Oleh Dokuka - WebFlux mastery

Comparison Resources

Community