Spring/Spring Boot

Spring Boot Main 실행흐름

helloJosh 2024. 7. 30. 00:44

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 스캔, 생성까지 다 해주는 걸로 알면될 것 같다.

다음 내용에는 스프링부트에서 어떻게 설정 관리하는 방법을 좀 공부할 생각이다.