Skip to content

Sn1perdog/mustinject

Repository files navigation

MustInject

A lightweight, Spring-inspired dependency injection framework built from scratch in Java
Showcasing graph algorithms, runtime bytecode manipulation, and IoC container design


🎯 Overview

Custom-built dependency injection framework to gain a deeper understanding of:

  • IoC container container design and how Spring's IoC container works under the hood
  • Graph theory for dependency resolution (topological sort in O(n+m))
  • Runtime proxy generation using Javassist bytecode manipulation
  • AOP principles implemented from scratch
  • Reflection API for annotation processing and classpath scanning

Built from first principles to master framework internals and design extensible, maintainable systems.


⭐ Technical Highlights

Graph-Based Dependency Resolution

Implements topological sorting using JGraphT to resolve complex dependency chains with automatic cycle detection:

// Automatically resolves this dependency chain:
ABC, ADC, ED
// Result: C, D, B, E, A (correct instantiation order)

Key Features:

  • Directed multigraph where edges represent dependencies
  • Detailed error reporting showing exact circular dependency paths
  • Ensures correct bean instantiation order for deep dependency trees

Runtime Proxy Generation with Javassist

Dynamic bytecode manipulation for transparent method interception:

Original BeanController ProxyInterception Proxy
// Each layer adds functionality while delegating to the previous

Capabilities:

  • Creates proxy classes at runtime without source code
  • Implements cross-cutting concerns (logging, timing, input mapping)
  • Preserves original bean behavior while adding new functionality

Intelligent Constructor Selection

  • Handles 0, 1, or multiple constructors with @Autowired disambiguation
  • Type-safe dependency injection through constructor parameters
  • Fail-fast validation during container initialization

Custom Annotation Processing

Spring-compatible annotations with extensible system:

  • Bean stereotypes: @Component, @Service, @Controller, @Repository
  • Method-level AOP: @InputMapping, @Timed
  • Efficient classpath scanning using Reflections library

Console Input Mapping

Unique @InputMapping annotation for automatic console input interception:

@Controller
public class AccountController {
    @InputMapping
    public void addAccount(String input) {
        // 'input' automatically populated from System.in
    }
}

🏗️ Architecture

Design Patterns: Factory • Orchestrator • DTO • Proxy • Singleton

Core Components:

mustinject-core/
├── MustInject.java              # IoC container & entry point
├── bean/
│   ├── BeanOrchestrator.java    # Dependency graph & topological sort
│   └── exception/               # CyclicalDependency, MultipleConstructor
├── proxy/
│   ├── ControllerProxyFactory   # Console input interception
│   └── InterceptionProxyFactory # Method-level AOP
└── annotation/                  # Spring-compatible annotations

Tech Stack: JGraphT • Javassist • Reflections • Lombok • Gradle


🎓 Skills Demonstrated

Advanced Java:

  • Reflection API mastery
  • Bytecode manipulation with Javassist
  • Graph algorithms (topological sort, cycle detection)
  • Design patterns in production scenarios

Software Engineering:

  • Separation of concerns with clean architecture
  • Fail-fast design with comprehensive error handling
  • Extensible annotation system
  • Performance metrics and optimization
  • SLF4J logging for debugging

Framework Development:

  • IoC container internals
  • AOP implementation from scratch
  • Annotation processing and classpath scanning
  • Proxy pattern for cross-cutting concerns

🔄 Comparison to Spring

Feature MustInject Spring Framework
Dependency Injection ✅ Constructor-based ✅ Constructor, Field, Setter
Cycle Detection ✅ Graph-based with paths ✅ Built-in
AOP Support ✅ Method interception ✅ Full AspectJ
Proxy Generation Javassist (subclass-based) JDK/CGLIB
Codebase Size ~1,500 LOC ~500,000+ LOC

🚀 Quick Start

Installation

./gradlew :mustinject-core:publishToMavenLocal
repositories {
    mavenCentral()
    mavenLocal()
}

dependencies {
    implementation 'be.kdg.sa.ap1:MustInject:1.2.0'
}

Usage

@Service
public class UserService {
    public void createUser(String name) { /* ... */ }
}

@Controller
public class UserController {
    private final UserService userService;
    
    @Autowired
    public UserController(UserService userService) {
        this.userService = userService;
    }
    
    @InputMapping
    @Timed
    public void handleInput(String input) {
        userService.createUser(input);
    }
}

public class Main {
    public static void main(String[] args) {
        MustInject.run(Main.class);
        var controller = (UserController) MustInject.retrieveBeanFromClass(UserController.class);
        controller.handleInput("placeholder");
    }
}

Testing

./gradlew test                    # Run tests
./gradlew :mustinject-demo:run    # Run demo
./gradlew :mustinject-demo:jar    # Build JAR

📂 Key Implementation Files

About

Dependency injection framework compatible with Spring annotations

Resources

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages