[F-Lab 챌린지 49일차 TIL]

성수데브리·2023년 8월 15일
0

f-lab_java

목록 보기
39/73
  • 애플리케이션 로직 빈
  • 애플리케이션 인프라스트럭처 빈
    • 빈 구성 정보에 의해 컨테이너에 등록되는 빈이지만 애플리케이션의 로직이 아니라 애플리케이션이 동작하는데 꼭 필요한 기술 기반을 제공하는 빈이다.
  • 컨테이너 인프라스트럭처 빈
    • 스프링 컨테이너 자신이거나 스프링 컨테이너의 기능을 확장해서 빈의 등록과 생성, 관계설정, 초기화 등의 작업에 참여하는 빈을 컨테이너 인프라 빈이라고 한다.
  • AutoConfiguration이란 건 미리 준비해둔 Configuration들을 스프링부트가 어떤 게 필요한지 판단을 해서 자동으로 선택해서 사용하게 해주는 것
@SpringBootApplication
public class HeeverseTicketApplication {

    public static void main(String[] args) {
        SpringApplication.run(HeeverseTicketApplication.class, args);
    }
}

// SpringApplication 생성
/**
	 * Static helper that can be used to run a {@link SpringApplication} from the
	 * specified sources using default settings and user supplied arguments.
	 * @param primarySources the primary sources to load
	 * @param args the application arguments (usually passed from a Java main method)
	 * @return the running {@link ApplicationContext}
	 */
	public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
		return new SpringApplication(primarySources).run(args);
	}

// SpringApplication.run() 내부 코드
/**
	 * Run the Spring application, creating and refreshing a new
	 * {@link ApplicationContext}.
	 * @param args the application arguments (usually passed from a Java main method)
	 * @return a running {@link ApplicationContext}
	 */
	public ConfigurableApplicationContext run(String... args) {
		long startTime = System.nanoTime();

		// DefaultBootstrapContext 생성
		DefaultBootstrapContext bootstrapContext = createBootstrapContext();
		ConfigurableApplicationContext context = null;
		configureHeadlessProperty();
		SpringApplicationRunListeners listeners = getRunListeners(args);
		listeners.starting(bootstrapContext, this.mainApplicationClass);
		try {
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
			ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
			Banner printedBanner = printBanner(environment);

			// 어플리케이션 컨텍스트 생성
			context = createApplicationContext();
			context.setApplicationStartup(this.applicationStartup);
			
			// 어플리케이션 컨텍스트 준비
			prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
			
			// 어플리케이션 컨텍스트 refresh
			refreshContext(context);
			
			// 어플리케이션 컨텍스트 refresh 후처리
			afterRefresh(context, applicationArguments);

			Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
			if (this.logStartupInfo) {
				new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup);
			}
			listeners.started(context, timeTakenToStartup);
			callRunners(context, applicationArguments);
		}
		catch (Throwable ex) {
			if (ex instanceof AbandonedRunException) {
				throw ex;
			}
			handleRunFailure(context, ex, listeners);
			throw new IllegalStateException(ex);
		}
		try {
			if (context.isRunning()) {
				Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);
				listeners.ready(context, timeTakenToReady);
			}
		}
		catch (Throwable ex) {
			if (ex instanceof AbandonedRunException) {
				throw ex;
			}
			handleRunFailure(context, ex, null);
			throw new IllegalStateException(ex);
		}
		return context;
	}

어플리케이션 컨텍스트 생성

// 어플리케이션 컨텍스트 생성
context = createApplicationContext();
context.setApplicationStartup(this.applicationStartup);

/**
	 * Strategy method used to create the {@link ApplicationContext}. By default this
	 * method will respect any explicitly set application context class or factory before
	 * falling back to a suitable default.
	 * @return the application context (not yet refreshed)
	 * @see #setApplicationContextFactory(ApplicationContextFactory)
	 */
	protected ConfigurableApplicationContext createApplicationContext() {
		return this.applicationContextFactory.create(this.webApplicationType);
	}

어플리케이션 컨텍스트 준비

// 어플리케이션 컨텍스트 준비
prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);

어플리케이션 컨텍스트 refresh

  • 빈들을 찾아서 등록하고 웹서버를 만들어 실행하는 등의 핵심 작업들이 진행
// 어플리케이션 컨텍스트 refresh
refreshContext(context);

어플리케이션 타입에 따라 refresh() 동작 내용이 다르므로 템플릿 메서드 패턴이다.

// AbstractApplicationContext

@Override
	public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
			StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");

			// Prepare this context for refreshing.
			prepareRefresh();

			// Tell the subclass to refresh the internal bean factory.
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

			// Prepare the bean factory for use in this context.
			prepareBeanFactory(beanFactory);

			try {
				// Allows post-processing of the bean factory in context subclasses.
				postProcessBeanFactory(beanFactory);

				StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");

				// context 에 등록된 빈팩토리 후처리기를 생성, 등록
				invokeBeanFactoryPostProcessors(beanFactory);

				// Register bean processors that intercept bean creation.
				registerBeanPostProcessors(beanFactory);
				beanPostProcess.end();

				// Initialize message source for this context.
				initMessageSource();

				// Initialize event multicaster for this context.
				initApplicationEventMulticaster();

				// Initialize other special beans in specific context subclasses.
				// 템플릿 메서드 
				// 톰캣 생성 -> 서블릿 컨텍스트 생성 
				onRefresh();

				// Check for listener beans and register them.
				registerListeners();

				// **Instantiate all remaining (non-lazy-init) singletons.**
				finishBeanFactoryInitialization(beanFactory);

				// Last step: publish corresponding event.
				finishRefresh();
			}

			catch (BeansException ex) {
				if (logger.isWarnEnabled()) {
					logger.warn("Exception encountered during context initialization - " +
							"cancelling refresh attempt: " + ex);
				}

				// Destroy already created singletons to avoid dangling resources.
				destroyBeans();

				// Reset 'active' flag.
				cancelRefresh(ex);

				// Propagate exception to caller.
				throw ex;
			}

			finally {
				// Reset common introspection caches in Spring's core, since we
				// might not ever need metadata for singleton beans anymore...
				resetCommonCaches();
				contextRefresh.end();
			}
		}
	}
// 어플리케이션 컨텍스트 refresh 후처리
afterRefresh(context, applicationArguments);

@SpringBootApplication

  • @SpringBootConfiguration, @EnableAutoConfiguration and @ComponentScan 가지의 역할을 수행하는 애노테이션

  • @SpringBootConfiguration

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    **@Configuration**
    @Indexed
    public @interface SpringBootConfiguration {
    • 애플리케이션에 1개만 존재해야 한다.
    • @Configuration 의 상위 어노테이션이지만 역할은 똑같다.
    • 해당 어노테이션을 기준으로 설정들을 불러올때 사용된다.
  • @EnableAutoConfiguration

    • 보통의 @Enable* 어노테이션을 이용하는 클래스는 @Import를 메타어노테이션으로 사용한다.

    • 자동 설정 기능을 활성화하는 어노테이션이다.

    • 특정 작업들을 위한 베이스 패키지

      @Target(ElementType.TYPE)
      @Retention(RetentionPolicy.RUNTIME)
      @Documented
      @Inherited
      @AutoConfigurationPackage
      @Import(AutoConfigurationImportSelector.class)
      public @interface EnableAutoConfiguration {

      AutoConfigurationImportSelector.selectImports()를 실행시켜서 반환한 Configuration 클래스만 로딩을 하도록 한다.

      @Override
      	public String[] selectImports(AnnotationMetadata annotationMetadata) {
      		if (!isEnabled(annotationMetadata)) {
      			return NO_IMPORTS;
      		}
      		AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
      		return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
      	}
    • 자동 설정이 가능한 목록은 spring-boot-autoconfigure.jar의 META-INF 디렉토리의 spring.factories에서 확인가능

    • Configuration 클래스들은 조건을 충족할 때만 로딩되게 설정할 수 있다.

      // 예시
      
      @AutoConfiguration
      @ConditionalOnClass(ObjectMapper.class)
      public class JacksonAutoConfiguration {
      • @ConditionalOnClass: 해당 클래스가 클래스 패스에 존재하는 경우
      • @ConditionlOnBean: 해당 클래스나 이름이 빈 팩토리에 포함되어 있는 경우
      • @ConditionalOnMissingBean: 해당 빈이 등록되어있지 않은 경우
        • 직접 설정한 빈과 자동 설정으로 등록되는 빈이 중복될 경우 직접 설정한 빈에 우선순위를 부여
  • @ComponentScan

    루트 패키지 이하의 클래스 중 @Component 어노테이션이 붙은 클래스들을 검색

    @Component의 종류

    1. @Contoller - @RestController
    2. @Service
    3. @Repository
    4. @Configuration
    • 3가지 종류의 컴포넌트가 다른 이름을 가진 이유는 레이어를 구분하기 위함
  • 스프링은 @Component 이 선언된 포함한 클래스들의 인스턴스를 자동으로 생성

0개의 댓글