IoC 컨테이너 (5) : ApplicationEventPublisher

de_sj_awa·2021년 6월 25일
0
post-custom-banner

10. ApplicationEventPublisher

이벤트 프로그래밍에 필요한 인터페이스 제공. 옵저버 패턴 구현체.

ApplicationContext extends ApplicationEventPublisher

  • publishEvent(ApplicationEvent event)

이벤트 만들기

  • ApplicationEvent 상속
  • 스프링 4.2 부터는 이 클래스를 상속받지 않아도 이벤트로 사용할 수 있다.
public class MyEvent extends ApplicationEvent {

    private int data;
    
    public MyEvent(Object source) {
        super(source);
    }
    
     public MyEvent(Object source, int data) {
        this.source = source;
        this.data = data;
    }
    
     public int getData(){
        return data;
    }
}

이 이벤트를 발생시키는 기능을 ApplicationContext가 가지고 있다.

이벤트 발생 시키는 방법

  • ApplicationEventPublisher.publishEvent();
@Component
public class AppRunner implements ApplicationRunner {

    /*
    @Autowired
    ApplicationContext applicationContext;
    */

    @Autowired
    ApplicationEventPublisher publishEvent;

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

이 이벤트를 처리하려면 이벤트 핸들러가 필요하고, 이벤트 핸들러는 빈으로 등록되어야 한다.

이벤트 처리하는 방법

  • ApplicationListener<이벤트> 구현한 클래스 만들어서 빈으로 등록하기.
  • 스프링 4.2 부터는 @EventListener를 사용해서 빈의 메소드에 사용할 수 있다.
  • 기본적으로는 synchronized.
  • 순서를 정하고 싶다면 @Order와 함께 사용.
  • 비동기적으로 실행하고 싶다면 @Async와 함께 사용.
  1. ApplicationListener<이벤트> 구현한 클래스 만들어서 빈으로 등록하기.
@Component
public class MyEventHandler implements ApplicationListener<MyEvent> {

    @Override
    public void onApplicationEvent(MyEvent myEvent) {
        System.out.println("이벤트 받았다. 데이터는 " + myEvent.getData());
    }
}
  1. 스프링 4.2 부터는 @EventListener를 사용해서 빈의 메소드에 사용할 수 있다.

이벤트를 발생시킨 소스 확인하기

public class MyEvent {

    private int data;

    private Object source;

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

    public Object getSource(){
        return source;
    }

    public int getData(){
        return data;
    }
}
@Component
public class MyEventHandler {

    @EventListener
    public void handle(MyEvent event){
        System.out.println("이벤트 받았다. 데이터는 " + event.getData());
    }

EventHandler가 여러가지 있다고 가정해보자.

@Component
public class MyEventHandler {

    @EventListener
    public void handle(MyEvent event){
        System.out.println(Thread.currentThread().toString());
        System.out.println("이벤트 받았다. 데이터는 " + event.getData());
    }
@Component
public class AnotherHandler {

    @EventListener
    public void handler(MyEvent myEvent){
        System.out.println(Thread.currentThread().toString());
        System.out.println("Another " + myEvent.getData());
    }
}

그러면 이 두 핸들러 모두 실행될 것이다. 기본적으로는 순차적으로 실행된다(synchronized). 이를 확인해보려면 스레드의 이름을 찍어보면 되는데, 둘 다 main 스레드에서 실행된다.

  1. 순서를 정하고 싶다면 @Order와 함께 사용.

순차적으로 실행되는 경우에는 Sequence를 지정해 줄 수 있다.

@Component
public class AnotherHandler {

    @EventListener
    @Order(Ordered.HIGHEST_PRECEDENCE + 2)
    public void handler(MyEvent myEvent){
        System.out.println(Thread.currentThread().toString());
        System.out.println("Another " + myEvent.getData());
    }
}
@Component
public class MyEventHandler {
	
    @EventListener
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public void handle(MyEvent event){
        System.out.println(Thread.currentThread().toString());
        System.out.println("이벤트 받았다. 데이터는 " + event.getData());
    }
  1. 비동기적으로 실행하고 싶다면 @Async와 함께 사용.

순서는 보장되지 않는다. 각각의 쓰레드 풀에서 따로 실행됨

@SpringBootApplication
@EnableAsync
public class Springtest6Application {

    public static void main(String[] args) {
        SpringApplication.run(Springtest6Application.class, args);
    }
}
@Component
public class AnotherHandler {

    @EventListener
    @Async
    public void handler(MyEvent myEvent){
        System.out.println(Thread.currentThread().toString());
        System.out.println("Another " + myEvent.getData());
    }
}
@Component
public class MyEventHandler {
	
    @EventListener
    @@Async
    public void handle(MyEvent event){
        System.out.println(Thread.currentThread().toString());
        System.out.println("이벤트 받았다. 데이터는 " + event.getData());
    }

스프링이 제공하는 기본 이벤트 (ApplicationContext) 관련

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

    @EventListener
    @Async
    public void handle(MyEvent event){
        System.out.println(Thread.currentThread().toString());
        System.out.println("이벤트 받았다. 데이터는 " + event.getData());
    }
    
   @EventListener
    @Async
    public void handle(ContextRefreshedEvent event){
        System.out.println(Thread.currentThread().toString());
        System.out.println("ContextRefreshedEvent");
        //var applicationContext = event.getApplicationContext()
    }
     
    @EventListener
    @Async
    public void handle(ContextClosedEvent event){
        System.out.println(Thread.currentThread().toString());
        System.out.println("ContextClosedEvent");
        //var applicationContext = event.getApplicationContext()
    }
}

참고

  • 인프런 : 스프링 프레임워크 핵심 기술(백기선)
profile
이것저것 관심많은 개발자.
post-custom-banner

0개의 댓글