본문 바로가기
Back-End/Spring

[Spring Boot] SpringApplication

by hongdor 2023. 3. 31.
728x90

Spring Boot의 공식 문서 :Spring Boot Reference Documentation

그 중 Documentation Overview 문서 : Documentation Overview (spring.io)

4번 항목에서 Spring Boot를 사용하려는 개발자는
SpringApplication 항목 문서를 읽어보라고 나와 있다

 

Spring Boot의 공식 문서 :Spring Boot Reference Documentation

그 중 Core Features 문서 : Core Features (spring.io)

Core Features의 1번 항목 : Core Features (spring.io) - SpringApplication

 

 

SpringApplication

SpringApplication 클래스는 Spring을 시작하는 편리한 방법을 제공한다.
많은 상황에서 SpringApplication.run 정적 메서드에 위임할 수 있다.
아래 이후 설명할 내용들은 SpringApplication 클래스가 제공하는 기능 및 특징 등에 대해 설명하는 것이다

@SpringBootApplication
public class MyApplication {

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

}

 

1. 시작 실패

  • 시작에 실패할 경우 FailureAnalyzer가 에러 메세지와 해결방법을 알려준다. 아래는 예시이다.
***************************
APPLICATION FAILED TO START
***************************

Description:

Embedded servlet container failed to start. Port 8080 was already in use.

Action:

Identify and stop the process that is listening on port 8080 or configure this application to listen on another port.

 

 

  • 만약 FailureAnalyzer가 알수 없는 에러라면 전체 내역을 볼 수 있다.
    속성파일 application.properties에 아래와 같이 추가해준다.
logging.level.root=debug

 

2. 지연 초기화(Lazy Initialization)

  • lazy initialization, 즉 지연 초기화가 활성화 되면, bean들이 시작 후에 필요한 경우에 생성된다.
  • 장점은 시작 시간이 짧아지는 것이다. web application에서 web 관련 bean들이 http 요청이 오기 전까지 초기화(생성)되지 않는다.
  • 단점, 빈들이 나중에 초기화되기 때문에 문제 발생의 확인이 늦어진다. 그래서 기본적으로는 Lazy initialization은 꺼져 있다.
  • 켜는 방법은
    1. SpringApplicationBuilder의 lazyInitialization 메소드 사용
    2. SpringApplication에서 setLazyInitialization 메소드 사용
    3. spring.main.lazy-initialization=true 설정 세가지 방법이 있다.
    Lazy initialization을 켜고 @Lazy(false)로 일부만 시작 시 초기화도 가능하다.

 

3. 배너 변경하기

  • classpath에 banner.txt를 추가하면 시작 시 나오는 배너를 바꿀 수 있다.
    또는 spring.banner.location에 파일경로를 추가하면 된다.
    파일은 UTF-8 이어야 한다. spring.banner.charset에 설정할 수 있다.
  • banner.txt 안에 application의 정보도 출력할 수 있다.
    예) ${spring-boot.version} 는 사용되는 spring boot 버전이 출력된다.
          >> "spring-boot-version : ${spring-boot.version}"
  • SpringApplication.setBanner(…) 또는 org.springframework.boot.Banner 인터페이스의 printBanner() 를 구현하면
    코드로도 banner 변경이 가능하다
  • spring.main.banner-mode 속성으로 log로 출력 여부까지 설정할 수 있다.(on, off로 설정)

 

4. SpringApplication 커스텀

  • SpringApplication의 기본 값을 커스텀하고 싶다면, instance를 만들고 커스텀하면 된다.
@SpringBootApplication
public class MyApplication {

    public static void main(String[] args) {
        SpringApplication application = new SpringApplication(MyApplication.class);
        application.setBannerMode(Banner.Mode.OFF);
        application.run(args);
    }

}

또는 application.lproperties 파일로 설정값을 바꾸는 방법도 있다

 

5. Fluent Builder API

  • 순차적인 빌드가 필요할 경우 SpringApplicationBuilder를 사용하면 된다.
    아래는 예시 2가지
new SpringApplicationBuilder()
        .sources(Parent.class)
        .child(Application.class)
        .bannerMode(Banner.Mode.OFF)
        .run(args);
public class App {
    public static void main(String[] args) {
        new SpringApplicationBuilder()
          .parent(ParentConfig.class).web(WebApplicationType.NONE)
          .child(WebConfig.class).web(WebApplicationType.SERVLET)
          .sibling(RestConfig.class).web(WebApplicationType.SERVLET)
          .run(args);
    }
}

 

 

6. Application 사용 가능 여부

”actuator”를 사용한다면 Spring의 정보를 읽을 수 있는 url endpint를 제공한다.

  • 1. Liveness state
    “Liveness” state는 잘 동작하고 있는지 또는 복구중인지를 알려준다.

    만약 “Liveness”가 망가졌다면, 복구를 못하고 있는것이다. 재시작이 필요하다.
    Spring Boot application의 상태는 ApplicationContext로 표현된다.
    Spring Boot는 application context가이 성공적으로 시작됐다면, 정상인 것으로 생각한다.
  • 2. Readiness State
    “Readiness” state는 트래픽을 다룰 준비가 돼있는지 말해준다.

    Readiness의 실패는 트래픽을 지금 처리할 수 없다는 것이다.
    주로 시작시에 CommandLineRunner와 ApplicationRunner가 동작 중이거나,
    application이 여유가 없다고 판단할 시에 발생한다.
  • 3. Managing the Application Availability State
    ApplicationAvailability 인터페이스를 주입 함으로써 현재 availability를 알아낼 수 있다.
    application의 state를 듣거나 업데이트하길 원할 때 아래와 같이 하면된다.
  • application에게 readiness state를 읽어서 파일로 내보내는 예시
@Component
public class MyReadinessStateExporter {

    @EventListener
    public void onStateChange(AvailabilityChangeEvent<ReadinessState> event) {
        switch (event.getState()) {
            case ACCEPTING_TRAFFIC:
                // create file /tmp/healthy
                break;
            case REFUSING_TRAFFIC:
                // remove file /tmp/healthy
                break;
        }
    }

}

 

 

  • application에게 회복 불가능한 상태(LivenessState.BROKEN)를 알리는 예시
@Component
public class MyLocalCacheVerifier {

    private final ApplicationEventPublisher eventPublisher;

    public MyLocalCacheVerifier(ApplicationEventPublisher eventPublisher) {
        this.eventPublisher = eventPublisher;
    }

    public void checkLocalCache() {
        try {
            // ...
        }
        catch (CacheCompletelyBrokenException ex) {
            AvailabilityChangeEvent.publish(this.eventPublisher, ex, LivenessState.BROKEN);
        }
    }

}

 

 

7. Application Events and Listeners

  • Spring Framework events에 더해, SpringApplication 클래스도 event들을 보낸다
  • 몇몇 event 들은 ApplicationContext가 생성되기 전에 발생해서
    이 event 들에 대한 listener를 Bean으로 등록할 수 없다.
  • 그럴땐 SpringApplication.addListeners(…) 메서드나 SpringApplicationBuilder.listeners(…)를 사용할 수 있다.
  • application 시작 시 event 발생 순서
    1. ApplicationStartingEvent : 시작. listener와 initializer 등록 전
    2. ApplicationEnvironmentPreparedEvent : Environment가 알려졌지만, context는 생성 전
    3. ApplicationContextInitializedEvent : ApplicationContext가 준비되고. ApplicationContextInitializers가 불러와졌으나, bean의 정의는 불러오기 전
    4. ApplicationPreparedEvent : refresh 는 시작됐으나 bean 정의는 불러오기 전
    5. ApplicationStartedEvent : context가 refresh 된 후, application 과 command-line runner는 불로오기 전
    6. AvailabiliyChangeEvent : LivenessState.CORRECT 바로 후에 발생
    7. ApplicationReadyEvent : an application & commad-line runner가 불러진 후
    8. AvailabilityChangeEvent : ReadinessState.ACCEPTING_TRAFFIC 바로후
    9. ApplicationFailedEvent : 시작 도 중 예외 발생 시

    위 이벤트 들은 SpringBoot의 SpringApplication와 연관된 이벤트만 포함한다.

    아래 이벤트들은 ApplicationPreparedEvent 후, ApplicationStartedEvent 전 사이에 발생
    1. WebServerInitializedEvent : WebServer가 준비 됐다는 이벤트
    Servelt은 ServletWebServerInitializedEvent
    Reactive는 ReactiveWebServerInitializedEvent
    2. ContextRefreshedEvent : ApplicationContext 가 refresh 됐을 때
  • Event Listener는 기본적으로 같은 스레드를 사용하기 때문에 무거운 작업을 하면 안된다.
    필요하면 대신에 command-line runner를 고려해보는 것이 좋다.(10번)
  • Application event는 Spring Frame work의 이벤트 발생 메커니즘을 사용했기 때문에
    하위 리스너에게 발생한 이벤트는 상위 리스너에게도 전달된다.
    위 두개를 구분하기 위해서는 context 비교를 해야한다.
    리스너의 context는 ApplicationContextAware를 구현하거나 리스너를 Bean으로 등록하면 주입할 수 있다.

 

8. Web Environment

  • SpringApplication은 올바른 ApplicationContext 타입을 만들기 위해서 노력한다.
  • 타입은 결정하기 위한 알고리즘은 아래와 같다.
    Spring MVC가 존재하면 AnnotationConfigServletWebServerApplicationContext 를 사용
    Spring MVC가 없고 WebFlux가 있으면, AnnotationConfigReactiveWebServerApplicationContext를 사용
    그 외 경우에는 AnnotationConfigApplicationContext 를 사용한다.
  • 즉, SpringMVC와 SpringWebClient를 함께 사용하면, 기본적으로 SpringMVC가 사용된다.
    override 하고 싶다면 setWebApplicationType(WebApplicationType)를 호출하면 된다.
    setApplicationContextFactory(...)를 호출하여 사용되는 ApplicationContext 유형을 완전히 제어할 수도 있다.

 

9. Accessing Application Arguments

  • SpringApplication.run(…)으로 전달되는 인자에 접근하고 싶다면, ApplicationArguments bean을 주입할 수 있다. ApplicationArguments interface는 String[] 과 parsed, non-parsed 모두 접근할 수 있다.
@Component
public class MyBean {

    public MyBean(ApplicationArguments args) {
        boolean debug = args.containsOption("debug");
        List<String> files = args.getNonOptionArgs();
        if (debug) {
            System.out.println(files);
        }
        // if run with "--debug logfile.txt" prints ["logfile.txt"]
    }

}

 

 

10. Using the ApplicationRunner or CommandLineRunner

  • ApplicationRunner or CommandLineRunner 인터페이스는 SpringApplication.run 전에 불려진다.
    (시작 후, 그리고 트래픽을 받기 전에 동작해야하는 것에 알맞다)
  • CommandLineRunner 인터페이스는 application arguments에 대한 접근을 제공한다.
  • 반면에 ApplicationRunner는 9번에서 보았던 ApplicationArguments를 사용한다.
@Component
public class MyCommandLineRunner implements CommandLineRunner {

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

}
  • 여러 CommandLineRunner or ApplicationRunner 빈들이 순차적으로 실행되야 한다면 org.springframework.core.Ordered 인터페이스 또는 org.springframework.core.annotation.Order 어노테이션을 사용하면 된다.

 

11. Application Exit

  • SpringApplication은 종료 시 ApplicationContext가 정상적으로 닫히도록 JVM에 종료 훅을 등록한다.
    그래서 모든 Spring은 lifecycle callback을 사용할 수 있다.
    (DisposableBean인터페이스 or @PreDestroy)
  • SpringApplication.exit() 이 불려진 후에 bean들이 구체적인 exit code를 반환하고 싶다면 org.springframework.boot.ExitCodeGenerator 인터페이스를 구현하면 된다.
    exit code는 System.exit()으로 전달될 수 있다.
@SpringBootApplication
public class MyApplication {

    @Bean
    public ExitCodeGenerator exitCodeGenerator() {
        return () -> 42;
    }

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

}

 

 

  • ExitCodeGenerator 인터페이스는 예외로 구현될 수 있다.
    어떤 예외가 발생했을 때 Spring Boot가 getExit() 메서드로 구현된 exitcode를 반환한다.
  • ExitCodeGenerator가 여러개라면 0이 아닌 첫번째 exit 코드가 사용된다.
  • generator들의 순서를 정하기 위해서 org.springframework.core.Ordered 인터페이스 또는 org.springframework.core.annotation.Order 어노테이션을 사용하면 된다.

 

12. Admin Features

  • spring.application.admin.enabled 속성으로 admin 기능들을 사용할 수 있다.
  • 이것은 SpringApplicationAdminMXBean을 MBeanServer에 노출시킨다.
  • 이 기능은 Spring Boot application을 원격으로 관리하는데 유용하다.

 

13. Application Startup tracking

  • application 시작 시, SpringApplication 과 ApplicationContext는
    application lifecycle, bean lifecycle, processing application event와 관련된 많은 일을 수행한다.
  • ApplicationStartup을 세팅하여 시간 과정을 추적할 수 있다.
    예를들어 BufferingApplicationStartup을 사용할 수 있다..
@SpringBootApplication
public class MyApplication {

    public static void main(String[] args) {
        SpringApplication application = new SpringApplication(MyApplication.class);
        application.setApplicationStartup(new BufferingApplicationStartup(2048));
        application.run(args);
    }

}
  • BufferingApplicationStartup은 시작과정을 저장하고 외부에 데이터를 제공할 수 있다.
    모든 컴포넌트에서 BufferingApplicationStartup 빈을 요청할 수 있다.
  • 그 외 FlightRecorderApplicationStartup 등의 구현체도 있다.
  • JSON 으로 정보를 제공하는 startup endpoint를 만드는 방법은 아래 참고
    Spring Boot Actuator Web API Documentation
728x90

댓글