springframework

springframeworko.s.w IoC 핵심기술. ApplicationEventPublisher

Jungsoomin :) 2020. 8. 21. 01:14

ApplicationContext 가 상속하는 또하나의 인터페이스, ApplicationEventPublisher에 알아본다.

  옵저버 패턴 기반의 구현체로서, 이벤트 기반 프로그래밍 시 유용하게 사용된다고 한다.

 

ApplicationEventListener 는 ApplicationContext 가 상속하고 있는 인터페이스 임으로 주입 받을 수 있다.

 

package me.soomin.demospring51;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationRunner;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component;


@Component
public class AppRunner2 implements ApplicationRunner {

    @Autowired
    ApplicationEventPublisher applicationEventPublisher;

}

 

 


이벤트 객체는 빈으로 등록하지 않아도 되며, 원하는 데이터를 필드로 지정하고, 생성자의 값으로 주어 데이터를 전달해 줄 수도 있다.

package me.soomin.demospring51;

public class MyEvent2 {

    private int data;

    private Object object;

    public MyEvent2(int data, Object object){
        this.data =data;
        this.object=object;
    }

    public int getData() {
        return data;
    }

    public Object getObject() {
        return object;
    }
}

이벤트를 발생시켜야 이벤트 객체의 값을 받아볼 수 있는데, 이 기능은 ApplicationContext, ApplicationEventListener 가 가지고 있다.

 ApplicationEventListener 의 pulblishEvent( Event ) 에 이벤트 클래스를 주고 데이터를 생성자로 주어, 이벤트를 발생시킨다.

@Autowired
    ApplicationEventPublisher applicationEventPublisher;

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

    }

 

이벤트를 핸들링 하려면, 핸들러, 즉  이벤트 컨트롤러가 필요하다. 핸들러는 반드시 빈으로 등록되어야한다. 

 

이벤트 핸들링 메서드에 @EventListner 에노테이션을 주어 이벤트를 핸들링 한다.

              파라미터는 핸들링하려고하는 이벤트이다.

@Component
public class AnotherHandler {

    @EventListener
    public void handle(MyEvent2 myEvent2){
        System.out.println(Thread.currentThread().toString());
        System.out.println("Another "+myEvent2.getObject());
    }
    
   }

기본적으로 퍼블리싱 되는 이벤트에 여러가지 핸들러를 동시에 적용시킬수 있는데, 이때 주의해야할 사항은 멀티스레딩이 아니라는 것이다

          즉 순차적이다. 

        여기에 순번을 주려면, AOP에서 사용했었던 @Order 에노테이션으로 순번을 주면 된다고 한다.

 

@Component
public class AnotherHandler {

    @EventListener
    @Order(1)
    public void handle(MyEvent2 myEvent2){
        System.out.println(Thread.currentThread().toString());
        System.out.println("Another "+myEvent2.getObject());
    }
    
    @EventListener
    @Order(2)
    public void handle1(MyEvent2 myEvent){
    	 System.out.println(Thread.currentThread().toString());
        System.out.println("Another "+myEvent.getObject());
    }
    
   }

이를 비동기적으로 이용하려면

이벤트 핸들링 메서드에 @Async 에노테이션을 붙이고 @Configuration 클래스에 @EnableAsync 에노테이션을 붙여야하며, 쓰레드 풀에서 돌기에 순번은 전혀 보장되지 못한다.

@Component
public class AnotherHandler {

    @EventListener
    @Async
    public void handle(MyEvent2 myEvent2){
        System.out.println(Thread.currentThread().toString());
        System.out.println("Another "+myEvent2.getObject());
    }
    
    @EventListener
    @Async
    public void handle1(MyEvent2 myEvent){
    	 System.out.println(Thread.currentThread().toString());
        System.out.println("Another "+myEvent.getObject());
    }
    
   }

@SpringBootApplication
@EnableAsync
public class Demospring51Application {

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

    }

}

여기서 생각해봐야할 점은, 스프링에서 기본적으로 제공하는 ApplicationEvent 가 존재한다는 것이다.

   ApplicationRefreshEvent 는 초기화 시점 , ApplicationClosedEvent 는 종료시점이다.

EventListener
    @Async
    /* @Order(Ordered.HIGHEST_PRECEDENCE+3)*/
    public void handle(ContextRefreshedEvent event){
        System.out.println(Thread.currentThread().toString());
        System.out.println("ContextRefreshedEvent");
    }

    @EventListener
    @Async
    /* @Order(Ordered.HIGHEST_PRECEDENCE+3)*/
    public void handle(ContextClosedEvent event){
        System.out.println(Thread.currentThread().toString());
        System.out.println("ContextClosedEvent");
    }

생명주기 관련하여 추가할 것이 있을시, 사용해보고 싶다.


스프링 4.2 이전 버전이하에서는 이벤트 핸들러는 ApplicationListener 를 구현해야 했으며, 이벤트 객체는 ApplicationEvent 를 구현해야 했다.

@Component
public class MyEventHandler implements ApplicationListener<MyEvent> {
    @Override
    public void onApplicationEvent(MyEvent event) {
        System.out.println("이벤트 받았다. 데이터는 "+event.getData());
    }
}
package me.soomin.demospring51;

import org.springframework.context.ApplicationEvent;

public class MyEvent extends ApplicationEvent {

    private int data;
    /**
     * Create a new {@code ApplicationEvent}.
     *
     * @param source the object on which the event initially occurred or with
     *               which the event is associated (never {@code null})
     */
    public MyEvent(Object source) {
        super(source);
    }
    public MyEvent(Object source,int data) {
        super(source);
        this.data = data;
    }

    public int getData() {
        return data;
    }
}