ποΈ IoC Container
The Inversion of Control (IoC) container is the core of the Sprout Framework. It manages the creation, dependency injection, and lifecycle of all application components.
Overviewβ
Sproutβs IoC container provides the following features:
- Component Scanning: Automatic detection of classes based on annotations using the Reflections library.
- Constructor Injection: Type-safe dependency resolution (field injection is not supported).
- Lifecycle Management: Phased bean creation, initialization, and destruction.
- Circular Dependency Detection: Topological sorting and cycle detection via BeanGraph.
- Order Support: Control of bean initialization and collection ordering with
@Order. - CGLIB Proxy: Ensures singleton behavior for
@Configurationclasses. - Extensibility via Strategy Pattern: Plugin-based structure for bean creation and dependency resolution strategies.
Container Architectureβ
Core Componentsβ
Sproutβs IoC container consists of the following key classes:
Context and Factoryβ
SproutApplicationContext: The main application context.DefaultListableBeanFactory: The core bean factory implementation.ClassPathScanner: Scans the classpath and generates bean definitions.BeanGraph: Manages dependency graphs and topological sorting.
Bean Creation Strategies (Strategy Pattern)β
BeanInstantiationStrategy: Interface for bean instantiation strategies.ConstructorBasedInstantiationStrategy: Creates beans via constructors.FactoryMethodBasedInstantiationStrategy: Creates beans via factory methods.
Dependency Resolution Strategies (Chain of Responsibility Pattern)β
DependencyResolver: Interface for dependency resolution.CompositeDependencyResolver: Combines multiple resolvers.
DependencyTypeResolver: Strategy for resolving dependencies by type.SingleBeanDependencyResolver: Resolves single bean dependencies.ListBeanDependencyResolver: ResolvesListtype dependencies.
Lifecycle Management (Phase Pattern)β
BeanLifecycleManager: Manages the execution of lifecycle phases.BeanLifecyclePhase: Interface for lifecycle phases.InfrastructureBeanPhase: Creates infrastructure beans (order=100).BeanPostProcessorRegistrationPhase: Registers BeanPostProcessors (order=200).ApplicationBeanPhase: Creates application beans (order=300).ContextInitializerPhase: Executes ContextInitializers (order=400).
Type Matching Serviceβ
BeanTypeMatchingService: Centralizes logic for type-based bean searching and matching.
Container Initialization Processβ
public class SproutApplication {
public static void run(Class<?> primarySource) throws Exception {
// 1. Configure packages to scan
List<String> packages = getPackagesToScan(primarySource);
// 2. Create application context
ApplicationContext applicationContext =
new SproutApplicationContext(packages.toArray(new String[0]));
// 3. Initialize context (refresh)
applicationContext.refresh();
// 4. Start server
HttpServer server = applicationContext.getBean(HttpServer.class);
server.start(port);
}
}
Component Scanningβ
Supported Annotationsβ
Sprout recognizes the following component annotations:
@Component // General component
@Service // Business logic layer
@Repository // Data access layer
@Controller // Web layer
@Configuration // Configuration class
@Aspect // AOP aspect
@ControllerAdvice // Global exception handling
@WebSocketHandler // WebSocket handler
Scanning Processβ
// Scanning logic in ClassPathScanner
public Collection<BeanDefinition> scan(ConfigurationBuilder configBuilder,
Class<? extends Annotation>... componentAnnotations) {
// 1. Find classes with annotations using Reflections
Set<Class<?>> componentCandidates = new HashSet<>();
for (Class<? extends Annotation> anno : componentAnnotations) {
componentCandidates.addAll(r.getTypesAnnotatedWith(anno));
}
// 2. Filter concrete classes (exclude interfaces and abstract classes)
Set<Class<?>> concreteComponentTypes = componentCandidates.stream()
.filter(clazz -> !clazz.isInterface() &&
!clazz.isAnnotation() &&
!Modifier.isAbstract(clazz.getModifiers()))
.collect(Collectors.toSet());
// 3. Find beans defined by @Bean methods
Set<Class<?>> configClasses = r.getTypesAnnotatedWith(Configuration.class);
for (Class<?> configClass : configClasses) {
for (Method method : configClass.getDeclaredMethods()) {
if (method.isAnnotationPresent(Bean.class)) {
beanMethodReturnTypes.add(method.getReturnType());
}
}
}
}
Enabling Component Scanningβ
Use @ComponentScan on the main application class:
@ComponentScan("com.myapp") // Scan a specific package
@ComponentScan({"com.myapp.web", "com.myapp.service"}) // Scan multiple packages
public class Application {
public static void main(String[] args) throws Exception {
SproutApplication.run(Application.class);
}
}