SproutApplicationContext.java
package sprout.context.builtins;
import org.reflections.scanners.Scanners;
import org.reflections.util.ClasspathHelper;
import org.reflections.util.ConfigurationBuilder;
import org.reflections.util.FilterBuilder;
import sprout.aop.annotation.Aspect;
import sprout.beans.BeanDefinition;
import sprout.beans.InfrastructureBean;
import sprout.beans.annotation.*;
import sprout.beans.processor.BeanDefinitionRegistrar;
import sprout.beans.processor.BeanPostProcessor;
import sprout.context.*;
import sprout.context.lifecycle.*;
import sprout.mvc.advice.annotation.ControllerAdvice;
import sprout.scan.ClassPathScanner;
import sprout.server.websocket.annotation.WebSocketHandler;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
public class SproutApplicationContext implements ApplicationContext {
private final DefaultListableBeanFactory beanFactory;
private final List<String> basePackages;
private final ClassPathScanner scanner;
private final BeanLifecycleManager lifecycleManager;
private List<BeanDefinition> infraDefs;
private List<BeanDefinition> appDefs;
public SproutApplicationContext(String... basePackages) {
this.beanFactory = new DefaultListableBeanFactory();
this.basePackages = List.of(basePackages);
this.scanner = new ClassPathScanner();
this.beanFactory.registerCoreSingleton("applicationContext", this);
// BeanLifecycleManager 초기화
List<BeanLifecyclePhase> phases = new ArrayList<>();
phases.add(new InfrastructureBeanPhase());
phases.add(new BeanPostProcessorRegistrationPhase());
phases.add(new ApplicationBeanPhase());
phases.add(new ContextInitializerPhase());
this.lifecycleManager = new BeanLifecycleManager(phases);
}
private void scanBeanDefinitions() throws NoSuchMethodException {
ConfigurationBuilder configBuilder = new ConfigurationBuilder();
for (String pkg : basePackages) {
configBuilder.addUrls(ClasspathHelper.forPackage(pkg));
}
configBuilder.addScanners(Scanners.TypesAnnotated, Scanners.SubTypes);
configBuilder.addClassLoaders(ClasspathHelper.contextClassLoader(), ClasspathHelper.staticClassLoader());
FilterBuilder filter = new FilterBuilder();
for (String pkg : basePackages) {
filter.includePackage(pkg);
}
configBuilder.filterInputsBy(filter);
beanFactory.registerSingletonInstance("container", this);
Collection<BeanDefinition> allDefs = scanner.scan(configBuilder,
Component.class,
Controller.class,
Service.class,
Repository.class,
Configuration.class,
Aspect.class,
ControllerAdvice.class,
WebSocketHandler.class
);
List<BeanDefinitionRegistrar> registrars = allDefs.stream()
.filter(bd -> BeanDefinitionRegistrar.class.isAssignableFrom(bd.getType()))
.map(bd -> {
try {
return (BeanDefinitionRegistrar) bd.getType().getDeclaredConstructor().newInstance();
} catch (Exception e) {
throw new RuntimeException("Failed to instantiate BeanDefinitionRegistrar: " + bd.getType().getName(), e);
}
})
.toList();
for (BeanDefinitionRegistrar registrar : registrars) {
allDefs.addAll(registrar.registerAdditionalBeanDefinitions(allDefs));
}
List<BeanDefinition> infraDefs = new ArrayList<>(allDefs.stream()
.filter(bd -> BeanPostProcessor.class.isAssignableFrom(bd.getType()) ||
InfrastructureBean.class.isAssignableFrom(bd.getType()))
.toList());
List<BeanDefinition> appDefs = new ArrayList<>(allDefs);
appDefs.removeAll(infraDefs);
this.infraDefs = infraDefs;
this.appDefs = appDefs;
}
@Override
public void refresh() throws Exception {
// 1. 빈 정의 스캔
scanBeanDefinitions();
// 2. BeanLifecycleManager를 통한 단계별 실행
BeanLifecyclePhase.PhaseContext context = new BeanLifecyclePhase.PhaseContext(
beanFactory,
infraDefs,
appDefs,
basePackages
);
lifecycleManager.executePhases(context);
}
@Override
public void close() {
beanFactory.reset();
}
@Override
public void reset() {
beanFactory.reset();
}
@Override
public Object getBean(String name) {
return beanFactory.getBean(name);
}
@Override
public <T> T getBean(Class<T> requiredType) {
return beanFactory.getBean(requiredType);
}
@Override
public Collection<Object> getAllBeans() {
return beanFactory.getAllBeans();
}
@Override
public <T> List<T> getAllBeans(Class<T> type) {
return beanFactory.getAllBeans(type);
}
@Override
public boolean containsBean(String name) {
return beanFactory.containsBean(name);
}
@Override
public void registerRuntimeBean(String name, Object bean) {
beanFactory.registerSingletonInstance(name, bean);
}
@Override
public void registerCoreSingleton(String name, Object bean) {
beanFactory.registerCoreSingleton(name, bean);
}
@Override
public CtorMeta lookupCtorMeta(Object bean) {
return beanFactory.lookupCtorMeta(bean);
}
}