Spring/Spring Boot

[Spring Boot] 2-1. Core Features: SpringApplication 클래스

noahkim_ 2023. 10. 8. 02:15

1. SpringApplication

  • SpringApplication 클래스는 Spring application을 편리하게 기동하는 방법을 제공하는 클래스입니다.
  • SpringApplication.run 메서드가 main method로부터 호출되어 bootstrapping 작업을 시작합니다.
    • 기본적으로 INFO 레벨의 로그가 찍이도록 설정됩니다.
    • startup과 연관된 메시지가 출력됩니다.

 

Startup Failure

  • FaliureAnalyzers는 Spring Boot 기동중 에러 발생 시 문제 해결을 담당합니다.
    • 여러 구현체를 가지고 있습니다.
    • 에러 메시지를 출력합니다. 
    • 에러를 처리할 수 없다면, 원인 파악을 위해 DEBUG 레벨의 로그를 확인해야 합니다.
  • DEBUG 레벨 로그는 Full Conditions Report를 제공합니다.
  • DEBUG 로그 레벨 설정방법
    • environment에 debug 속성 주기
    • java argument에 --debug 추가
    • Condition Evaluation Report의 속성값을 debug로 주기
      • ConditionEvaluationReport: 어떤 auto-configuration이 활성화되었는지 확인하는 기능

 

Lazy Initialization

  • SpringApplication 클래스는 지연 초기화 기능을 지원합니다.
  • 지연 초기화는 빈들이 애플리케이션이 시작될 때 초기화 되는것이 아닌 필요로 하는 시점에 객체가 초기화되는 기능입니다.
  • 장점
    • 애플리케이션이 기동되는 시간을 줄여줍니다.
    • 빈들은 필요로 할때 (HTTP Request로 인한 필요) 초기화됩니다.
  • 단점
    • 객체 생성 코드에 대한 문제를 늦게 발견합니다.
      • 잘못 셋팅된 빈이 지연 초기화 된다면 기동시에 문제점이 발견되지 않고 필요에 의해 초기화될 때 발견됩니다.
    • 필요한 시점에 초기화되므로 메모리 용량이 모든 빈의 생성을 수용할 수 있어야 합니다.
      • 런타임에 메모리 부족으로 인해 서버가 죽을 수 있습니다.
      • 이를 고려하여 서버 기동 전 JVM의 Heap 사이즈가 충분하게 할당되어야 합니다.
  • 지연 초기화 활성화 방법
    • SpringApplicationBuilder의 lazyInitialization() 을 통한 SpringApplication 생성
    • SpringApplication의 setLazyInitialization() 호출
    • spring.main.lazy-initialization 속성 사용
    • @Lazy 어노테이션 사용 (특정 빈에서만 지연 초기화 설정)

 

Application Availability

  • 관리 플랫폼에서 애플리케이션이 배포될 경우, Spring Application은 플랫폼에게 availability 정보를 제공해야 합니다
  • Spring Boot는 health endpoint를 제공하는 actuator 모듈을 지원합니다.
    • actuator 모듈은 두가지 availability 정보를 제공합니다 (liveness, readiness)

 

1. Liveness State

  • 정상 Liveness State는 애플리케이션이 내부에서 '정상 동작' 중이거나 '에러를 스스로 회복할 수 있는 상태'를 의미합니다.
  • 손상 Liveness State는 '에러를 스스로 회복할 수 없는 상태'를 뜻합니다.
  • Liveness State는 외부 시스템의 상태를 기반해서 결정되면 안됩니다.
    • Spring Application의 상태가 정상이더라도 외부 시스템이 에러 상황인 경우 손상 Liveness State로 판명됩니다.
    • 이러한 경우 Spring Application에서 의존하는 외부 시스템 전체를 연쇄적으로 재실행 시켜야 할 수 있습니다.
    • Spring Application의 상태는 ApplicationContext 기반으로 표현되어야 합니다.
      • 보통 ApplicationContext가 리프레쉬 후에 Spring Application의 상태는 정상이라 간주됩니다.

 

2. Readiness State

  • 정상 Readiness State는 애플리케이션이 정상적으로 트래픽을 처리할 수 있는 상태를 의미합니다.
  • 손상 Readiness State는 애플리케이션이 정상적으로 트래픽을 처리할 수 없는 상태를 의미합니다.
    • 애플리케이션에 트래픽이 몰릴때의 상태입니다.
    • 보통 애플리케이션이 실행중 상태입니다.
      • CommandLineRunner 혹은 ApplicationRunner가 실행중인 상황입니다.
      • Spring Component 라이프사이클의 콜백인 @PostConstruct를 뜻하지 않습니다.

 

3. Managing the Application Availability State

  • ApplicationAvailability를 사용하면 현재 애플리케이션의 가용성 상태를 조회할 수 있습니다.

 

Application Events and Listeners

  • SpringApplication 클래스는 애플리케이션의 시작 프로세스를 관리합니다
    • 이벤트 드리븐 매커니즘으로 초기화 작업을 수행합니다.
    • 애플리케이션 기동시 ApplicationEvent를 발행합니다.
  • 몇몇의 ApplicationEvent는 ApplicationContext 초기화 전의 이벤트를 가집니다.
  • ApplicationListener는 이러한 ApplicationEvent를 listen하는데 쓰입니다.
    • META-INF/spring.factories 파일에 작성하여 등록하기
    • environment에 org.springframework.context.ApplicationListener을 key로 한 구현체를 등록하기
    • SpringApplication 클래스로 ApplicationListener 등록하기

 

ApplicationEvent 발행순서

  1. ApplicationStartingEvent
    애플리케이션이 시작될 때 발생합니다. (Listener와 Initializer의 등록만 이루어진 상태)

  2. ApplicationEnvironmentPreparedEvent
    사용될 Environment가 알려져 있지만, 아직 ApplicationContext가 생성되기 전의 상태입니다.

  3. ApplicationContextInitializedEvent
    ApplicationContext가 준비되고 ApplicationContextInitializers가 호출되었으나 빈 정의가 로드되기 전의 상태입니다.

  4. ApplicationPreparedEvent
    빈 정의들이 로드된 후, refresh가 시작되기 전에 발생합니다.
    (refresh를 시도하며 WebServer를 초기화합니다)

    4-1. WebServerInitializedEvent : WebServer가 초기화 된 후 발생합니다.
    4-2. ContextRefreshedEvent : Bean이 모두 초기화되어 ApplicationContext가 refresh 될 때 발생합니다.

  5. ApplicationStartedEvent
    ApplicationContext가 refresh 된 후, ApplicationRunner 혹은 CommandLineRunner가 호출되기 전에 발생합니다.

  6. ApplicationChangeEvent (LivenessState.CORRECT)
    애플리케이션이 정상적으로 실행중임을 나타낼 때 발생합니다. 

  7. ApplicationReadyEvent
    ApplicationRunner 혹은 CommandLineRunner가 호출된 후 발생합니다.

  8. AvailabilityChangeEvent (ReadinessState.ACCEPTING_TRAFFIC)
    애플리케이션이 서비스 요청을 처리할 준비가 되었음을 나타내기 위해 발생합니다 

  9. ApplicationFailedEvent
    애플리케이션의 시작 중에 예외가 발생하면 트리거됩니다.

 

 
  • ApplicationEvent는 Spring Framework의 이벤트 발행 매커니즘을 통해 발행됩니다.
  • 발행된 ApplicationEvent는 등록된 Listener들에게 전달됩니다.
  • ApplicationContext는 계층 구조를 가지며
    • 자식 ApplicationContext에서 ApplicationEvent 발행하면 부모 ApplicationContext에 ApplicationEvent가 전파됩니다.
    • ApplicationContext의 Listener는 전달받은 ApplicationEvent가 자신이 발행한건지 전파된건지 구분해야 합니다.
    • 이를 위해 Listener는 자신이 속한 ApplicationContext의 참조를 가지고 있어야 합니다.
      • Listener는 ApplicationContextAware 인터페이스를 구현함으로써 자신의 ApplicationContext 참조 가질 수 있습니다
      • Listener가 빈으로 등록되어 있다면 @Autowired로 ApplicationContext를 주입받을 수 있습니다.

 

Web Environment

  • SpringApplication은 현재 애플리케이션에 적절한 ApplicationContext 타입인 WebApplicationType을 정해줍니다.
    • Spring MVC 를 사용한다면 AnnotationConfigServletWebServerApplicationContext 타입을 사용합니다.
    • Spring MVC, Spring WebFlux 동시에 사용한다면 AnnotationConfigServletWebServerApplicationContext
    • Spring WebFlux를 사용한다면 AnnotationConfigReactiveWebServerApplicationContext 타입을 사용합니다.
    • 다른 경우라면 AnnotationConfigApplicationContext 타입을 사용합니다.
  • SpringApplication은 setWebApplicationType()을 호출하여 명시적으로 지정할 수 있습니다.

 

Accessing Application Arguments

  • Spring Boot 실행 시, 명령행의 인자는 ApplicationArguments 타입의 빈으로 등록됩니다.
  • Environment는 CommandLinePropertySource를 의존성으로 등록합니다.
  • 이로 인해 @Value 어노테이션으로 프로퍼티 값을 주입받을 수 있습니다.

 

Using the ApplicationRunner or CommandLineRunner

  • Spring Boot 실행 시 코드를 실행시키고 싶다면 ApplicationRunner 혹은 CommandLineRunner를 사용하면 된다.
    • ApplicationContext의 빈 초기화가 끝난 후 ApplicationRunner 혹은 CommandLineRunner 코드가 실행된다
  • 순서를 정의하고 싶을 경우, Ordered 인터페이스나 @Order 를 붙여 선언하면 된다.
  • ApplicationRunner는 명령행의 인자를 ApplicationArguments 빈으로 주입받을 수 있다.
  • CommandLineRunner는 run 함수의 파라미터로 String 배열을 주입받을 수 있다
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;

@Component
public class MyCommandLineRunner implements CommandLineRunner {

    @Override
    public void run(String... args) {
        // Do something...
    }

}

 

 

 

참고