Main 흐름
Spring Boot에서 Main은 정말 큰 역할을 한다고 개인적으로 생각하고 있다. 따라서 오늘은 Main에 있는 코드를 좀 따라서 볼 생각이다.
@SpringBootApplication
@SpringBootConfiguration
- Spring Boot의 특수한 설정 클래스임을 나타내는 애너테이션 (@Component 를 포함)
@ComponentScan
- Spring에게 지정된 패키지와 그 하위 패키지들을 스캔하도록 지시하는 어노테이션
- 스프링이 컴포넌트를 자동으로 찾아서 스프링 애플리케이션 컨텍스트에 빈으로 등록할 위치를 지정한다.
@EnableAutoConfiguration
- Spring Boot의 자동 구성 기능을 활성화
Spring Boot main에 붙어있는 어노테이션은 이정도가 있으며 다음 게시글에 어노테이션 관련해서 더 설명할 예정입니다.
url : https://hellojosh.tistory.com/33
SpringApplication.run()
1. main안에 run()함수를 여러가지 run()이 overloading되어있지만 전부 밑에 와같이 ConfigurableApplicationContext를 반환하는 것을 알수있다. 또한 DefaultBootStrapContext를 생성해서 이걸 사용해 ApplicationContext를 만들어주고 반환하는 것을 알 수 있다.
public class SpringApplciation{
public ConfigurableApplicationContext run(String... args) {
DefaultBootstrapContext bootstrapContext = this.createBootstrapContext();
ConfigurableApplicationContext context = null;
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
context = this.createApplicationContext();
context.setApplicationStartup(this.applicationStartup);
this.refreshContext(context);
this.prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
} catch (Throwable var10) {
ex = var10;
throw this.handleRunFailure(context, ex, listeners);
}
try {return context;} catch (Throwable var9) {}
//중간중간 중략.. 필요한 코드만 추가했음
}
}
2. 그리고 그 refreshContext()를 까보면 AbstractApplicationContext안에서 beanFactory를 생성하는 것을 알 수 있다.
public class AbstractApplicationContext{
public void refresh() throws BeansException, IllegalStateException {
this.startupShutdownLock.lock();
try {
this.startupShutdownThread = Thread.currentThread();
this.invokeBeanFactoryPostProcessors(beanFactory); // bean scan 밑에서 설명
this.registerBeanPostProcessors(beanFactory);
beanPostProcess.end();
this.initMessageSource();
this.initApplicationEventMulticaster();
this.onRefresh();
this.registerListeners();
this.finishBeanFactoryInitialization(beanFactory); // bean create 밑에서 설명
this.finishRefresh();
//중략...
}
}
}
3. 그리고 빈 스캔은 invokeBeanFactoryPostPrcocessors를 들어가서 PostProcessorRegistrationDelegat.class 안에 postProcessBeanDefinitionRegistry()에서 ConfigurationClassPostProcessor.class안에 processConfigBeanDefinitions()에서 ConfigurationClassParser.class로 들어가 doProcessConfiguartionClass() 함수에서 딱봐도 reflection 기능을 사용해 bean을 전부 scan해서가져오는 걸 알 수 있다.
pubic class PostProcessorRegistrationDelgate{
public static void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {
Set<String> processedBeans = new HashSet();
// 중략..
while(var6.hasNext()) {
BeanFactoryPostProcessor postProcessor = (BeanFactoryPostProcessor)var6.next();
if (postProcessor instanceof BeanDefinitionRegistryPostProcessor registryProcessor) {
registryProcessor.postProcessBeanDefinitionRegistry(registry);
registryProcessors.add(registryProcessor);
} else {
regularPostProcessors.add(postProcessor);
}
}
//중략..
}
public class ConfigurationClassPostProcessor{
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
List<BeanDefinitionHolder> configCandidates = new ArrayList();
String[] candidateNames = registry.getBeanDefinitionNames();
String[] var4 = candidateNames;
int var5 = candidateNames.length;
//중략..
ConfigurationClassParser parser = new ConfigurationClassParser(this.metadataReaderFactory, this.problemReporter, this.environment, this.resourceLoader, this.componentScanBeanNameGenerator, registry);
Set<BeanDefinitionHolder> candidates = new LinkedHashSet(configCandidates);
Set<ConfigurationClass> alreadyParsed = new HashSet(configCandidates.size());
//중략...
}
}
public class ConfigurationClassParser{
@Nullable
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter) throws IOException {
if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
this.processMemberClasses(configClass, sourceClass, filter);
}
// 중략
}
}
4. 마지막으로 빈 생성은 위에서 AbstractApplicationContext의 finishBeanFactoryInitialization() 다음>DefaultListableBeanFactory.calss의 preInstantiateSingletons()을 들어간다. 그리고 AbstractBeanFactory.class의 getBean() -> doGetBean() 안에 CreateBean이 있는 걸 알 수 있다.
public class AbstractBeanFactory{
protected <T> T doGetBean(String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly) throws BeansException {
// 중략...
var4 = this.createBean(beanName, mbd, args);
// 중략...
return this.adaptBeanInstance(name, beanInstance, requiredType);
}
갑자기 코드 따라가느라 내용이 많아졌는데
요약하자면 SpringApplication.run()이 실행되면 아래 4가지가 생성된다.
밑의 순서로 메소드가 실행되면서 생성된다.
1. ApplicationContext
SpringApplication.run()
SpringApplication.createApplicationContext()
DefaultApplicationContextFactory.create()
DefaultApplicationContextFactory.createDefaultApplicationContext()
2. BeanFactory 생성
SpringApplication.run()
SpringApplication.refreshContext()
SpringApplication.refresh()
AbstractApplicationContext.refresh()
AbstractRefreshableApplicationContext.refreshBeanFactory()
3. BeanScan
SpringApplication.run()
SpringApplication.refreshContext()
SpringApplication.refresh()
AbstractApplicationContext.refresh()
AbstractApplicationContext.invokeBeanFactoryPostProcessors()
PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors()
ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry()
ConfigurationClassPostProcessor.processConfigBeanDefinitions()
ConfigurationClassParser.doProcessConfigurationClass()
4. bean 생성
SpringApplication.run()
SpringApplication.refreshContext()
SpringApplication.refresh()
AbstractApplicationContext.refresh()
DefaultListableBeanFactory.preInstantiateSingletons()
AbstractBeanFactory.getBean()
AbstractBeanFactory.doGetBean()
AbstractAutowireCapableBeanFactory.createBean()
AbstractAutowireCapableBeanFactory.doCreateBean()
main에서 ApplicationContext, Bean Factory, Bean 스캔, 생성까지 다 해주는 걸로 알면될 것 같다.
다음 내용에는 스프링부트에서 어떻게 설정 관리하는 방법을 좀 공부할 생각이다.
'Spring > Spring Boot' 카테고리의 다른 글
Spring Boot AOP (0) | 2024.07.30 |
---|---|
Spring Boot 자동 구성과 조건(@EnableAutoConfiguration) (0) | 2024.07.30 |
Spring DI(의존성 주입) (0) | 2024.07.29 |
Spring IoC(제어의 역전) (0) | 2024.07.28 |
Spring Boot 소개 (0) | 2024.07.27 |