A lightweight, Spring-inspired dependency injection framework built from scratch in Java
Showcasing graph algorithms, runtime bytecode manipulation, and IoC container design
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.
Implements topological sorting using JGraphT to resolve complex dependency chains with automatic cycle detection:
// Automatically resolves this dependency chain:
A → B → C, A → D → C, E → D
// 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
Dynamic bytecode manipulation for transparent method interception:
Original Bean → Controller Proxy → Interception Proxy
// Each layer adds functionality while delegating to the previousCapabilities:
- Creates proxy classes at runtime without source code
- Implements cross-cutting concerns (logging, timing, input mapping)
- Preserves original bean behavior while adding new functionality
- Handles 0, 1, or multiple constructors with
@Autowireddisambiguation - Type-safe dependency injection through constructor parameters
- Fail-fast validation during container initialization
Spring-compatible annotations with extensible system:
- Bean stereotypes:
@Component,@Service,@Controller,@Repository - Method-level AOP:
@InputMapping,@Timed - Efficient classpath scanning using Reflections library
Unique @InputMapping annotation for automatic console input interception:
@Controller
public class AccountController {
@InputMapping
public void addAccount(String input) {
// 'input' automatically populated from System.in
}
}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
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
| 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 |
./gradlew :mustinject-core:publishToMavenLocalrepositories {
mavenCentral()
mavenLocal()
}
dependencies {
implementation 'be.kdg.sa.ap1:MustInject:1.2.0'
}@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");
}
}./gradlew test # Run tests
./gradlew :mustinject-demo:run # Run demo
./gradlew :mustinject-demo:jar # Build JAR- BeanOrchestrator.java - Topological sorting & dependency graph
- ControllerProxyFactory.java - Javassist bytecode manipulation
- MustInject.java - IoC container orchestration
- InterceptionProxyFactory.java - AOP method interception