SpringBoot 소스 분석의 SpringBoot 시작 과정을 모르시면 안 됩니다.

43419 단어 필기
SpringBoot의 시작은 간단합니다. 코드는 다음과 같습니다.
@SpringBootApplication
public class MyApplication {
     
    public static void main(String[] args) {
     
        SpringApplication.run(MyApplication.class, args);
    }
}

코드에서 보듯이 SpringApplication의 정적 방법인 run을 호출했습니다.이 런 방법은 SpringApplication의 실례를 구성한 다음에 이 실례의 런 방법을 호출하면 SpringBoot을 시작합니다.
따라서 SpringBoot의 시작 과정을 분석하려면 SpringApplication의 구조 과정과 SpringApplication의run 방법의 실행 과정을 숙지해야 한다.
위의 코드를 예로 들어 SpringBoot의 부팅 프로세스를 분석합니다.
SpringApplication 구성 프로세스
SpringApplication 구조를 구성할 때 내부에 private 메서드인 initialize가 호출됩니다.
public SpringApplication(Object... sources) {
     
  initialize(sources); // sources     MyApplication class  
}

private void initialize(Object[] sources) {
     
  if (sources != null && sources.length > 0) {
     
    this.sources.addAll(Arrays.asList(sources)); //  sources   SpringApplication sources   ,      MyApplication   
  }
  this.webEnvironment = deduceWebEnvironment(); //      web  (javax.servlet.Servlet org.springframework.web.context.ConfigurableWebApplicationContext           ),    webEnvironment   
  //  spring.factories     key ApplicationContextInitializer          SpringApplication initializers   。                    
  setInitializers((Collection) getSpringFactoriesInstances(
      ApplicationContextInitializer.class));
  //  spring.factories     key ApplicationListener          SpringApplication listeners   。                    
  setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
  //   main ,   MyApplication 
  this.mainApplicationClass = deduceMainApplicationClass();
}

ApplicationContextInitializer, 애플리케이션 초기화기,
public interface ApplicationContextInitializer<C extends ConfigurableApplicationContext> {
     
	void initialize(C applicationContext);
}

ApplicationListener, 애플리케이션 이벤트(Application Event) 모니터:
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
     
	void onApplicationEvent(E event);
}

응용 프로그램 이벤트(Application Event)에는 응용 프로그램 시작 이벤트(Application Started Event), 실패 이벤트(Application Failed Event), 준비 이벤트(Application Prepared Event) 등이 있습니다.
프로그램 이벤트 감청기와 감청 이벤트는 귀속되어 있습니다.예를 들어Config ServerBootstrap Application Listener는 Application Environment Prepared Event 이벤트만 귀속시키고,Liquibase Service Locator Application Listener는 Application Started Event 이벤트만 귀속시키고,Logging Application Listener는 모든 이벤트와 귀속시킨다.
기본적으로, initialize 방법은spring에서.factories 파일에서 찾은 키가 ApplicationContextInitializer인 클래스는 다음과 같습니다.
org.springframework.boot.context.config.DelegatingApplicationContextInitializer org.springframework.boot.context.ContextIdApplicationContextInitializer org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer org.springframework.boot.context.web.ServerPortInfoApplicationContextInitializer org.springframework.boot.autoconfigure.logging.AutoConfigurationReportLoggingInitializer
키가 ApplicationListener인 경우:
org.springframework.boot.context.config.ConfigFileApplicationListener org.springframework.boot.context.config.AnsiOutputApplicationListener org.springframework.boot.logging.LoggingApplicationListener org.springframework.boot.logging.ClasspathLoggingApplicationListener org.springframework.boot.autoconfigure.BackgroundPreinitializer org.springframework.boot.context.config.DelegatingApplicationListener org.springframework.boot.builder.ParentContextCloserApplicationListener org.springframework.boot.context.FileEncodingApplicationListener org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener
SpringApplication 실행
런 방법을 분석하기 전에 SpringApplication의 일부 사건과 감청기 개념을 살펴보자.
먼저 SpringApplicationRunListeners 클래스와 SpringApplicationRunListener 클래스에 대한 소개입니다.
SpringApplicationRunListeners 내부에는 SpringApplicationRunListener 집합과 로그 클래스 1개가 있습니다.SpringApplicationRunListener 감청기의 대량 실행에 사용됩니다.
SpringApplication RunListener는 이름만 봐도 SpringApplication을 감청하는 런 방법의 실행을 알 수 있습니다.
5단계를 정의합니다.
1.started (run 방법이 실행될 때 즉시 실행되며 이벤트의 유형은 ApplicationStarted Event)
2.environmentPrepared(ApplicationContext가 생성되기 전에 환경 정보가 준비되었을 때 호출됩니다. 대응하는 이벤트의 유형은 ApplicationEnvironmentPreparedEvent)
3. contextPrepared (ApplicationContext가 만들어지고 소스가 불러오기 전에 한 번 호출됩니다. 구체적인 이벤트가 없습니다)
4.contextLoaded(ApplicationContext가 생성되고 로드된 후refresh 이전에 호출됩니다. 해당 이벤트의 유형은 ApplicationPreparedEvent)
5.finished(run 메서드가 종료되기 전에 호출됨. 해당 이벤트의 유형은 ApplicationReady Event 또는 ApplicationFailed Event)
Spring Application RunListener는 현재 하나의 실현 클래스 Event Publishing RunListener만 있습니다. 감청 과정을 Spring Application Event 이벤트로 봉하여 내부 속성(속성명 multicaster) Application Event Multicaster 인터페이스의 실현 클래스인 Simple Application Event Multicaster를 방송합니다.브로드캐스트된 이벤트 객체는 SpringApplication의 listeners 속성에 의해 처리됩니다.
그러니까 Spring Application RunListener와 Application Listener 간의 관계는 Application Event Multicaster가 방송한 Spring Application Event를 통해 연결된다.
SpringApplication의 run 메서드 코드는 다음과 같습니다.
public ConfigurableApplicationContext run(String... args) {
     
  StopWatch stopWatch = new StopWatch(); //            
  stopWatch.start(); //     ,      
  ConfigurableApplicationContext context = null;
  configureHeadlessProperty();
  //   SpringApplicationRunListeners,      EventPublishingRunListener
  SpringApplicationRunListeners listeners = getRunListeners(args);
  //      ,    SpringApplicationEvent         SpringApplication  listeners   
  //     ApplicationStartedEvent   listener        
  listeners.started();
  try {
     
    //              
    ApplicationArguments applicationArguments = new DefaultApplicationArguments(
        args);
    //   Spring  
    context = createAndRefreshContext(listeners, applicationArguments);
    //                 
    afterRefresh(context, applicationArguments);
    //    ApplicationReadyEvent           
    listeners.finished(context, null);
    stopWatch.stop(); //     ,      
    if (this.logStartupInfo) {
     
      new StartupInfoLogger(this.mainApplicationClass)
          .logStarted(getApplicationLog(), stopWatch);
    }
    return context; //   Spring  
  }
  catch (Throwable ex) {
     
    handleRunFailure(context, listeners, ex); //                  、     ApplicationFailedEvent           
    throw new IllegalStateException(ex);
  }
}

컨테이너를 만드는 방법createAndRefreshContext는 다음과 같습니다.
private ConfigurableApplicationContext createAndRefreshContext(
    SpringApplicationRunListeners listeners,
    ApplicationArguments applicationArguments) {
     
  ConfigurableApplicationContext context; //   Spring  
  //            。   web  ,  StandardServletEnvironment;  ,  StandardEnvironment
  ConfigurableEnvironment environment = getOrCreateEnvironment();
  //         。  profile,     
  configureEnvironment(environment, applicationArguments.getSourceArgs());
  //    ApplicationEnvironmentPreparedEvent           
  listeners.environmentPrepared(environment);
  //        
  if (isWebEnvironment(environment) && !this.webEnvironment) {
     
    environment = convertToStandardEnvironment(environment);
  }

  if (this.bannerMode != Banner.Mode.OFF) {
      //              banner
    printBanner(environment);
  }

  // Create, load, refresh and run the ApplicationContext
  context = createApplicationContext(); //   Spring  
  context.setEnvironment(environment); //   Spring       
  postProcessApplicationContext(context); //     ,Spring             
  applyInitializers(context); // SpringApplication          
  //     SpringApplicationRunListener contextPrepared  。               Spring   
  listeners.contextPrepared(context);
  if (this.logStartupInfo) {
     
    logStartupInfo(context.getParent() == null);
    logStartupProfileInfo(context);
  }

  //              Spring   ,       
  context.getBeanFactory().registerSingleton("springApplicationArguments",
      applicationArguments);

  Set<Object> sources = getSources();
  Assert.notEmpty(sources, "Sources must not be empty");
  load(context, sources.toArray(new Object[sources.size()]));
  //    ApplicationPreparedEvent           
  listeners.contextLoaded(context);

  // Spring     
  refresh(context);
  if (this.registerShutdownHook) {
     
    try {
     
      context.registerShutdownHook();
    }
    catch (AccessControlException ex) {
     
      // Not allowed in some environments.
    }
  }
  return context;
}

Spring 컨테이너의 createApplicationContext 작성 방법은 다음과 같습니다.
protected ConfigurableApplicationContext createApplicationContext() {
     
  Class<?> contextClass = this.applicationContextClass;
  if (contextClass == null) {
     
    try {
     
      //    web  ,    org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext  
      //     org.springframework.context.annotation.AnnotationConfigApplicationContext  
      contextClass = Class.forName(this.webEnvironment
          ? DEFAULT_WEB_CONTEXT_CLASS : DEFAULT_CONTEXT_CLASS);
    }
    catch (ClassNotFoundException ex) {
     
      throw new IllegalStateException(
          "Unable create a default ApplicationContext, "
              + "please specify an ApplicationContextClass",
          ex);
    }
  }
  return (ConfigurableApplicationContext) BeanUtils.instantiate(contextClass);
}

Spring 컨테이너를 만든 후 콜백 방법postProcessApplicationContext:
protected void postProcessApplicationContext(ConfigurableApplicationContext context) {
     
	if (this.webEnvironment) {
      //    web  
		if (context instanceof ConfigurableWebApplicationContext) {
      //     Spring Web  
			ConfigurableWebApplicationContext configurableContext = (ConfigurableWebApplicationContext) context;
			if (this.beanNameGenerator != null) {
      //   SpringApplication           ,   Spring   
				configurableContext.getBeanFactory().registerSingleton(
						AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR,
						this.beanNameGenerator);
			}
		}
	}
	if (this.resourceLoader != null) {
      //   SpringApplication        ,   Spring   
		if (context instanceof GenericApplicationContext) {
     
			((GenericApplicationContext) context)
					.setResourceLoader(this.resourceLoader);
		}
		if (context instanceof DefaultResourceLoader) {
     
			((DefaultResourceLoader) context)
					.setClassLoader(this.resourceLoader.getClassLoader());
		}
	}
}

초기화기가 하는 작업, 예를 들어ContextIdApplicationContextInitializer는 응용 프로그램의 id를 설정합니다.AutoConfigurationReportLoggingInitializer는 응용 프로그램에 조건 메모 파서 보고서 등을 추가합니다.
protected void applyInitializers(ConfigurableApplicationContext context) {
     
  //         ,      initialize  
  for (ApplicationContextInitializer initializer : getInitializers()) {
     
    Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(
        initializer.getClass(), ApplicationContextInitializer.class);
    Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
    initializer.initialize(context);
  }
}

스프링 용기의 리셋 Refresh 방법은 내부에서 많은 일을 할 수 있다. 예를 들어 BeanFactory 설정, BeanFactory Post Processor 인터페이스의 집행, BeanPost Processor 인터페이스의 집행, 자동화 설정 유형의 해석, 조건 주석의 해석, 국제화의 초기화 등이다.이 부분의 내용은 뒷글에서 해설할 것이다.
run 메서드의 Spring 컨테이너 생성이 완료되면 다음과 같은 afterRefresh 메서드가 호출됩니다.
protected void afterRefresh(ConfigurableApplicationContext context,
     ApplicationArguments args) {
     
   afterRefresh(context, args.getSourceArgs()); //        
   callRunners(context, args); //   Spring    ApplicationRunner CommandLineRunner      
 }

 private void callRunners(ApplicationContext context, ApplicationArguments args) {
     
	List<Object> runners = new ArrayList<Object>();
   //   Spring   ApplicationRunner      
	runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
   //   Spring   CommandLineRunner      
	runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
   //  runners    
	AnnotationAwareOrderComparator.sort(runners);
   //   runners    
	for (Object runner : new LinkedHashSet<Object>(runners)) {
     
		if (runner instanceof ApplicationRunner) {
      //    ApplicationRunner,  ApplicationRunner run    
			callRunner((ApplicationRunner) runner, args);
		}
		if (runner instanceof CommandLineRunner) {
      //    CommandLineRunner,  CommandLineRunner run    
			callRunner((CommandLineRunner) runner, args);
		}
	}
}

이렇게 런 방법이 완성되면스프링 용기도 이미 초기화가 완료됐고, 각종 감청기와 초기화기도 그에 상응하는 작업을 했다.
총결산
SpringBoot을 시작할 때 어떤 방법을 사용하든지 SpringApplication의 실례를 만들고 이 실례의run 방법을 호출하면 SpringBoot을 시작하는 것을 의미합니다.
런 방법이 호출되기 전, 즉 SpringApplication을 구성할 때 초기화 작업을 하고, 초기화할 때 다음과 같은 몇 가지 일을 한다.
1. 매개 변수 소스를 SpringApplication 속성에 설정합니다. 이 소스는 모든 종류의 매개 변수가 될 수 있습니다.본문의 예에서 이 소스가 바로 My Application의class 대상이다
2. 웹 프로그램인지 아닌지를 판단하고 웹 환경 이boolean 속성에 설정합니다
3. 모든 초기화기를 찾아 기본적으로 5개로 인티리어스 속성에 설정
4. 모든 응용 프로그램 감청기를 찾아 기본적으로 9개로listeners 속성에 설정
5. 실행 중인 주 클래스 찾기 (main class)
SpringApplication 구조가 완성된 후에 런 방법을 호출하고 SpringApplication을 시작합니다. 런 방법이 실행될 때 다음과 같은 몇 가지 일을 합니다.
1. SpringApplication의 실행을 관찰하기 위해 StopWatch를 구성한다
2. 모든 SpringApplicationRunListener를 찾아내서 SpringApplicationRunListeners에 봉하여 런 방법의 실행을 감청하는 데 사용한다.감청하는 과정에서 이벤트로 봉하여 초기화 과정에서 발생하는 프로그램 감청기를 감청하도록 방송합니다
3. Spring 컨테이너(Application Context)를 구성하고 반환
3.1 Spring 용기를 만드는 판단이 웹 환경인지 여부입니다. 그렇다면 Annotation Config Embedded Web Application Context를 구성하고,그렇지 않으면 구조 Annotation Config Application Context 3.2 초기화 과정에서 발생하는 초기화기는 이때부터 3.3 Spring 용기의 리셋(bean의 해석 완성, 각종 프로세스 인터페이스의 실행, 조건 주석의 해석 등)을 시작합니다. Spring 용기에서 Application Runner와Command Line Runner 인터페이스의 실현 유형을 찾아내고 정렬한 후 순서대로 실행합니다.
프로그래머의 이야기, 건더기, 자료를 공유하다.일선 공장 면접문제, 고발발 등 주류 기술 자료: JSYH1w

좋은 웹페이지 즐겨찾기