2020-3-30 다시 시작하는 스프링(6) - 이벤트퍼블리셔

AppicationEvenetPublisher

  • 옵저버 패턴의 구현체. 이벤트 프로그래밍에 필요한 인터페이스 제공
  • spring4.2이후로는 ApplicationEvenet를 구현할 필요가 없어졌다.
@Component
public class AppRunner implements ApplicationRunner {
    @Autowired
    private ApplicationEventPublisher publisher;

    @Override
    public void run(ApplicationArguments args) throws Exception {
        publisher.publishEvent(new MyEvenet(this, 100));
    }
}

public class MyEvenet {
    private int data;

    private Object source;

    public MyEvenet(Object source, int data) {
        this.source = source;
        this.data = data;
    }

    public int getData() {
        return data;
    }
}

@Component
public class MyEventHandler {

    @EventListener
    @Async
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public void handle(MyEvenet myEvenet) {
        System.out.println(Thread.currentThread().toString());
        System.out.println("이벤트 받았다. 데이터는 : " + myEvenet.getData());
    }

    @EventListener
    @Async
    public void handle(ContextRefreshedEvent event) {
        System.out.println(Thread.currentThread().toString());
        System.out.println("ConxtextRefreshedEvent");
    }

    @EventListener
    @Async
    public void handle(ContextClosedEvent event) {
        System.out.println(Thread.currentThread().toString());
        System.out.println("ConxtextClosedEvent");
    }
}

@Component
public class AnotherHandler {

    @EventListener
    public void handle(MyEvenet myEvenet) {
        System.out.println(Thread.currentThread().toString());
        System.out.println("Another Handler : " + myEvenet.getData());
    }
}
  • 기존에는 implement ApplicationEventPublisher라는 인터페이스를 구현해야됬지만, 4.2이후로는 저렇게 해도 된다. 코드에 스프링 코드를 침투시키지 않는것이 POJO다움이다?

이벤트 발생 및 처리

  • ApplicationEventPublisher.publishEvent()를 통해 발생
  • ApplicationListener<이벤트>구현클래스 만들어서 빈으로 등록하기
  • 기본적으로 synchronized
  • 이벤트 헨들러의 경우 @Bean으로 등록을 해야한다. 메서드에 @EventListener라는 어노테이션을 붙이면 된다. 다른 이벤트 헨들러를 만들면, 순차적으로 실행된다.
  • @Order를 붙여서 이벤트헨들러에 대해 순서를 정할 수 있다.
  • @Async를 붙여서 비동기적으로 처리할 수 있다. 이때는 순서보장이 안된다. 하지만 좀 더 부수적인 설정을 해줘야 다른 쓰레드에 돈다. main클래스에 @EnableAsync를 붙여야한다.

스프링이 제공하는 기본 이벤트

  • ContextRefreshedEvenet: ApplicationContext를 초기화 했거나 리프래시할때 발생
  • ContextStartedEvenet : ApplicationContext를 start()하여 라이프사이클 빈들이 시작신호를 받은 시점에 발생
  • ContextStoppedEvenet: ApplicationContext를 stop()하여 빈들이 정지할때 신호를 받은 시점에 발생
  • ContextClosedEvenet : ApplicationContext를 close()하여 싱글톤 빈들이 소멸되는 시점에 발생
  • RequestHandlerEvenet: HTTP요청을 처리했을때 발생
@Component
public class MyEventHandler {

    @EventListener
    @Async
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public void handle(MyEvenet myEvenet) {
        System.out.println(Thread.currentThread().toString());
        System.out.println("이벤트 받았다. 데이터는 : " + myEvenet.getData());
    }

    @EventListener
    @Async
    public void handle(ContextRefreshedEvent event) {
        System.out.println(Thread.currentThread().toString());
        System.out.println("컨텍스트 리프레시 이벤트 발생 ConxtextRefreshedEvent");
        System.out.println(event.toString());
    }

    @EventListener
    @Async
    public void handle(ContextStartedEvent event) {
        System.out.println(Thread.currentThread().toString());
        System.out.println("컨텍스트 시작 이벤트 ConxtextStartEvent");
        System.out.println(event.toString());
    }

    @EventListener
    @Async
    public void handle(ContextClosedEvent event) {
        System.out.println(Thread.currentThread().toString());
        System.out.println("컨텍스트 종료 이벤트 ConxtextClosedEvent");
        System.out.println(event.toString());
    }
}
  • 이 코드를 작성하고 실행을 시켜봤다.
  • 스프링실행순서
    • DeomoApplication을 실행
    • active profile이 있으면 설정하고 없으면 default profile설정
    • AnnotationConfigServletWebServerApplicationContext 리프레시
    • 톰캣시작 포트 8080매핑 => 톰캣서비스 시작 => 서브릿엔진 시작
    • filter매핑(characterEncodingFilter, hiddenHttpMethodFilter, httpPutFormContenetFilter, requestContextFilter, springSecurityFilter)
    • dispatcherServlet 매핑
    • JPA컨테이너 빌드(Building)=> 하이버네이트 코어, 설정, 어노테이션들 설정
    • Create Filter chain 여기에 필터와 인터셉트도 있다. 뭐가 먼저지?
    • 컨텍스트 리프레시 이벤트 발생 !!
    • 톰캣 8080에 시작 => DemoApplication 시작
    • 이벤트발생
    • closing AnnotationConfigServletWebServerApplicationContext
    • 컨텍스트종료 이벤트
    • clsoing JPA EntityManagerFactory

=> 이거를 이해해야한다.!!!

public @interface Order {
    int value() default 2147483647;
}

public interface Ordered {
    int HIGHEST_PRECEDENCE = -2147483648;
    int LOWEST_PRECEDENCE = 2147483647;

    int getOrder();
}
  • 이벤트헨들러의 우선순위를 결정하는 Order어노테이션은 안에 value를 전달받는다. value의 기본값은 위와같이 -21억~+21억의 범위가 있다.
Written on March 30, 2020