• 티스토리 홈
  • 프로필사진
    알쓸개잡
  • 방명록
  • 공지사항
  • 태그
  • 블로그 관리
  • 글 작성
알쓸개잡
  • 프로필사진
    알쓸개잡
    • 분류 전체보기 (93)
      • 스프링부트 (52)
      • AWS (5)
      • 쿠버네티스 (7)
      • 자바 (19)
      • 인프라 (1)
      • ETC (8)
  • 방문자 수
    • 전체:
    • 오늘:
    • 어제:
  • 최근 댓글
      등록된 댓글이 없습니다.
    • 최근 공지
        등록된 공지가 없습니다.
      • 반응형
      # Home
      # 공지사항
      #
      # 태그
      # 검색결과
      # 방명록
      • application events and listeners
        2023년 10월 02일
        • 알쓸개잡
        • 작성자
        • 2023.10.02.:34
        반응형

        Spring framework는 애플리케이션 기동시 ContextRefreshedEvent 외에도 몇 가지 추가적인 이벤트를 전송한다. 이번 포스팅에서는 애플리케이션 기동시 발생하는 events와 이에 대한 listeners에 대해서 기록한다.

         

        일부 event는 ApplicationContext가 생성되기 전에 발생하므로 해당 event에 대한 리스너를 @Bean으로 등록할 수 없다. ApplicationContext가 생성되기 전에 발생하는 event listener를 등록하려면 SpringApplication.addListeners(...) 메서드 또는 SpringApplicationBuilder.listeners(...) 메서드를 이용하여 등록할 수 있다.
        애플리케이션이 생성되는 방식에 관계없이 해당 listener가 자동으로 등록되도록 하려면 resources/META-INF/spring.factories 파일을 추가하고 org.springframework.context.ApplicationListener 키를 사용하여 listener를 등록할 수 있다.
        e.g) org.springframework.context.ApplicationListener=\
          com.example.event.listener.ApplicationEnvironmentPreparedEventListener, \
          com.example.event.listener.ApplicationContextInitializedEventListener, \
          com.example.event.listener.ApplicationPreparedEventListener

         

        애플리케이션 이벤트 순서

        애플리케이션 event는 애플리케이션 실행에 따라 다음 순서로 트리거 된다.

        • ApplicationStartingEvent
          • 애플리케이션 실행이 시작될 때 전송되지만 listener와 initializer의 등록을 제외한 모든 처리가 이루어지기 전에 전송된다.
        • ApplicationEnvironmentPreparedEvent
          • context가 생성되기 전 context 내의 Envirionment가 사용할 준비가 되었을때 전송된다.
        • ApplicationContextInitializedEvent
          • ApplicationContext가 준비되고 ApplicationContextInitializers가 호출되었지만 빈 정의가 로드되기 전에 전송된다.
        • ApplicationPreparedEvent
          • 빈 정의가 로드된 이후 ApplicationContext가 refresh 되기 직전에 전송된다.
        • ApplicationStartedEvent
          • ApplicationContext가 refresh 되었고 ApplicationRunner 혹은 CommandLineRunner가 호출되기 전에 전송된다.
        • AvailabilityChangeEvent
          • 애플리케이션이 라이브 상태임을 알리는 LivenessState.CORRECT 상태가 된 후에 전송된다.
        • ApplicationReadyEvent
          • ApplicationRunner 혹은 CommandLineRunner가 호출된 후에 전송된다.
        • AvailabilityChangeEvent
          • 애플리케이션이 요청을 처리할 준비가 되었음을 알리는 ReadinessState.ACCEPTING_TRAFFIC 상태가 된 후에 전송된다.
        • ApplicationFailedEvent
          • 시작시 예외가 발생한 경우에 전송된다.

        아래의 event는 ApplicationPreparedEvent 이후와 ApplicationStartedEvent 이전에 전송된다.

        • WebServerInitializedEvent
          • 웹서버가 준비된 후 전송된다. ServletWebServerInitializedEvent와 ReactiveWebServerInitializedEvent는 각각 servlet 및 reactive 방식에 따라서 전송된다.
        • ContextRefreshedEvent
          • ApplicationContext가 refresh 될 때 전송된다.

         

        Event listener는 기본적으로 동일한 스레드에서 실행되므로 Event listener에서 기동시 추가적인 작업을 수행하는 것보다  ApplicationRunner 혹은 CommandLineRunner를 통해서 추가적인 작업을 수행하는 것이 좋다.

        애플리케이션 이벤트는 Spring framework의 event publishing 메커니즘을 사용하여 전송된다. 이 메커니즘의 일부는 자식 context의 listener에 게시된 event가 모든 상위 context의 listener에도 게시되도록 보장한다. 그 결과, 애플리케이션이 SpringApplication 인스턴스 계층 구조를 사용하는 경우 listener는 동일한 유형의 애플리케이션 이벤트 인스턴스를 여러 개 수신할 수 있다.

         

        listener가 해당 context의 event와 하위 context의 event를 구분할 수 있도록 하려면 listener에 ApplicationContext를 주입되도록 한 다음 주입된 context를 event의 context와 비교해야 한다. ApplicationContext는 ApplicationContextAware를 implements 하여 주입하거나 listener가 bean인 경우 @Autowired를 사용하여 주입할 수 있다.

         

        Listener 구현 샘플

        ApplicationEnvirionmentPreparedEvent, ApplicationContextInitializedEvent, ApplicationPreparedEvent는 resources/META-INF/spring.factories에 listener를 등록하거나 SpringApplication의 addListeners 메소드를 통해서 listener를 등록해야 event를 받을 수 있다.

        org.springframework.context.ApplicationListener=\
          com.example.event.listener.ApplicationEnvironmentPreparedEventListener, \
          com.example.event.listener.ApplicationContextInitializedEventListener, \
          com.example.event.listener.ApplicationPreparedEventListener

         

        package com.example.event.listener;
        
        import lombok.RequiredArgsConstructor;
        import lombok.extern.slf4j.Slf4j;
        import org.springframework.beans.BeansException;
        import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent;
        import org.springframework.context.ApplicationContext;
        import org.springframework.context.ApplicationContextAware;
        import org.springframework.context.ApplicationListener;
        
        @RequiredArgsConstructor
        @Slf4j
        public class ApplicationEnvironmentPreparedEventListener implements
        	ApplicationListener<ApplicationEnvironmentPreparedEvent>, ApplicationContextAware {
        	private ApplicationContext applicationContext;
        
        	@Override
        	public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
        		log.info("received application environment prepared event, application context={}", applicationContext);
        	}
        
        	@Override
        	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        		this.applicationContext = applicationContext;
        	}
        }

         

        package com.example.event.listener;
        
        import lombok.RequiredArgsConstructor;
        import lombok.extern.slf4j.Slf4j;
        import org.springframework.beans.BeansException;
        import org.springframework.boot.context.event.ApplicationPreparedEvent;
        import org.springframework.context.ApplicationContext;
        import org.springframework.context.ApplicationContextAware;
        import org.springframework.context.ApplicationListener;
        import org.springframework.stereotype.Component;
        
        @RequiredArgsConstructor
        @Slf4j
        public class ApplicationPreparedEventListener implements
        	ApplicationListener<ApplicationPreparedEvent>, ApplicationContextAware {
        	private ApplicationContext applicationContext;
        
        	@Override
        	public void onApplicationEvent(ApplicationPreparedEvent event) {
        		log.info("received application prepared event, application context={}, event context={}",
        			applicationContext, event.getApplicationContext());
        	}
        
        	@Override
        	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        		this.applicationContext = applicationContext;
        	}
        }

         

        package com.example.event.listener;
        
        import lombok.RequiredArgsConstructor;
        import lombok.extern.slf4j.Slf4j;
        import org.springframework.beans.BeansException;
        import org.springframework.boot.context.event.ApplicationContextInitializedEvent;
        import org.springframework.boot.context.event.ApplicationPreparedEvent;
        import org.springframework.context.ApplicationContext;
        import org.springframework.context.ApplicationContextAware;
        import org.springframework.context.ApplicationListener;
        
        @RequiredArgsConstructor
        @Slf4j
        public class ApplicationContextInitializedEventListener implements
        	ApplicationListener<ApplicationContextInitializedEvent>, ApplicationContextAware {
        	private ApplicationContext applicationContext;
        
        	@Override
        	public void onApplicationEvent(ApplicationContextInitializedEvent event) {
        		log.info("received application context initialized event, application context={}, event context={}",
        			applicationContext, event.getApplicationContext());
        	}
        
        	@Override
        	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        		this.applicationContext = applicationContext;
        	}
        }

         

        WebServerInitializedEvent부터는 @EventListener 어노테이션 지정으로 event를 수신할 수 있다.

        package com.example.event.listener;
        
        import lombok.RequiredArgsConstructor;
        import lombok.extern.slf4j.Slf4j;
        import org.springframework.boot.availability.AvailabilityChangeEvent;
        import org.springframework.boot.availability.LivenessState;
        import org.springframework.boot.availability.ReadinessState;
        import org.springframework.boot.context.event.ApplicationReadyEvent;
        import org.springframework.boot.context.event.ApplicationStartedEvent;
        import org.springframework.boot.web.context.WebServerInitializedEvent;
        import org.springframework.context.ApplicationContext;
        import org.springframework.context.event.ContextRefreshedEvent;
        import org.springframework.context.event.EventListener;
        import org.springframework.stereotype.Component;
        
        @Slf4j
        @RequiredArgsConstructor
        @Component
        public class AfterWebServerInitializedEventListener {
        	private final ApplicationContext applicationContext;
        
        	@EventListener
        	public void onWebServerInitializedEvent(WebServerInitializedEvent event) {
        		log.info("received web server initialized event by event listener");
        		log.info("application context={}, event context={}", applicationContext, event.getApplicationContext());
        	}
        
        	@EventListener
        	public void onContextRefreshedEvent(ContextRefreshedEvent event) {
        		log.info("received context refreshed event by event listener");
        		log.info("application context={}, event context={}", applicationContext, event.getApplicationContext());
        	}
        
        	@EventListener
        	public void onApplicationStartedEvent(ApplicationStartedEvent event) {
        		log.info("received application started event by event listener");
        		log.info("application context={}, event context={}", applicationContext, event.getApplicationContext());
        	}
        
        	@EventListener
        	public void onLivenessEvent(AvailabilityChangeEvent<LivenessState> event) {
        		switch (event.getState()) {
        			case CORRECT -> log.info("received LivenessState.CORRECT event");
        			case BROKEN -> log.error("liveness probe failed");
        			default -> log.error("undefined liveness availability state");
        		}
        	}
        
        	@EventListener
        	public void onApplicationReadyEvent(ApplicationReadyEvent event) {
        		log.info("received application ready event by event listener");
        		log.info("application context={}, event context={}", applicationContext, event.getApplicationContext());
        	}
        
        	@EventListener
        	public void onReadinessEvent(AvailabilityChangeEvent<ReadinessState> event) {
        		switch (event.getState()) {
        			case ACCEPTING_TRAFFIC -> log.info("received ReadinessState.ACCEPTING_TRAFFIC event");
        			case REFUSING_TRAFFIC -> log.error("readiness probe failed");
        			default -> log.error("undefined readiness availability state");
        		}
        	}
        }

         

        위와 같이 event listener를 등록 후에 애플리케이션을 실행하면 아래와 같은 순서로 event가 발생 함을 확인할 수 있다.

        //ApplicationContext가 생성되기 전이므로 application context는 null 이다.
        licationEnvironmentPreparedEventListener : received application environment prepared event, application context=null
        //ApplicationContext가 생성되기 전이므로 application context는 null 이다.
        plicationContextInitializedEventListener : received application context initialized event, application context=null, event context=...AnnotationConfigServletWebServerApplicationContext@4a85eec0
        c.e.e.l.ApplicationPreparedEventListener : received application prepared event, application context=...AnnotationConfigServletWebServerApplicationContext@4a85eec0, event context=...AnnotationConfigServletWebServerApplicationContext@4a85eec0
        l.AfterWebServerInitializedEventListener : received web server initialized event by event listener
        l.AfterWebServerInitializedEventListener : received context refreshed event by event listener
        l.AfterWebServerInitializedEventListener : received application started event by event listener
        l.AfterWebServerInitializedEventListener : received LivenessState.CORRECT event
        l.AfterWebServerInitializedEventListener : received application ready event by event listener
        l.AfterWebServerInitializedEventListener : received ReadinessState.ACCEPTING_TRAFFIC event

        참고링크

        https://docs.spring.io/spring-boot/docs/current/reference/html/features.html#features.spring-application.application-events-and-listeners

         

        Core Features

        Spring Boot uses Commons Logging for all internal logging but leaves the underlying log implementation open. Default configurations are provided for Java Util Logging, Log4j2, and Logback. In each case, loggers are pre-configured to use console output with

        docs.spring.io

         

        반응형
        저작자표시 비영리 변경금지 (새창열림)

        '스프링부트' 카테고리의 다른 글

        spring boot kafka 연동하기  (0) 2023.10.08
        spring bean scope  (0) 2023.10.03
        spring boot JMS activemq connection factory  (0) 2023.09.28
        Spring Boot AutoConfiguration 간단 모듈 만들어보기  (0) 2023.09.24
        spring boot actuator health endpoints delay check  (0) 2023.09.22
        다음글
        다음 글이 없습니다.
        이전글
        이전 글이 없습니다.
        댓글
      조회된 결과가 없습니다.
      스킨 업데이트 안내
      현재 이용하고 계신 스킨의 버전보다 더 높은 최신 버전이 감지 되었습니다. 최신버전 스킨 파일을 다운로드 받을 수 있는 페이지로 이동하시겠습니까?
      ("아니오" 를 선택할 시 30일 동안 최신 버전이 감지되어도 모달 창이 표시되지 않습니다.)
      목차
      표시할 목차가 없습니다.
        • 안녕하세요
        • 감사해요
        • 잘있어요

        티스토리툴바