본문 바로가기

개발/Spring

IoC - ApplicationEventPublisher

반응형

ApplicationEventPublisher
 - ApplicationEventPublisher는 Spring의 ApplicationContext가 상속하는 인터페이스 중 하나.
 - 스프링 이벤트 프로그래밍에 필요한 인터페이스 
 - 옵저버 패턴 구현체 

1. ApplicationEvent 상속받은 Event 선언

package com.bpkim.demospring51;

import org.springframework.context.ApplicationEvent;

public class MyEvent extends ApplicationEvent {

    private int data;


    public MyEvent(Object source) {
        super(source);
    }

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

    public int getData() {
        return data;
    }
}

2. 이벤트 발생 시키기 

package com.bpkim.demospring51;

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

@Component
public class AppRunner implements ApplicationRunner {

    @Autowired
    ApplicationEventPublisher publishEvent;

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

3. 이벤트 핸들러 생성

package com.bpkim.demospring51;

import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;

@Component
public class MyEventHandler implements ApplicationListener<MyEvent> {

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

- 실행

*** 스프링 4.2 부터는 ApplicationEvent을 상속 받지 않고 Event 를 만들 수 있고, 
    ApplicationListener를 상속하지 않고 핸들러를 만들 수 있다.
     핸들러를 만들때는 핸들러 함수에 @EventLisiener를 이용해 빈 메소드에등로록 하여 사용한다.

1. 이벤트 
 - ApplicationEvent 상속제거 
    스프링 소스가 들어가있지 않은 비 침투성으로 스프링이 추구하는 방향!!

package com.bpkim.demospring51;

public class MyEvent {

    private int data;
    private Object source;

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

    public int getData() {
        return data;
    }
}

2. 핸들러
 - ApplicationListener 를 상속하여 특정 함수를 구현하지 않고 핸들러 함수에 @EventListener 를 이용하여 사용

package com.bpkim.demospring51;

import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;

@Component
public class MyEventHandler{

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

 

핸들러 여러개 등록 

1. 핸들러 함수 추가 

package com.bpkim.demospring51;

import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;

@Component
public class AnotherHandler {

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

- 실행 결과

핸들러는 순차적으로 실행되나 어느것이 먼저 실행되는지는 모른다. 
@Order 를 이용하여 우선순위를 정할 수 있다.

1. 핸들러 우선순위 정하기 
  - MyEventHandler의 우선순위를 우선 하기 

package com.bpkim.demospring51;

import org.springframework.context.event.EventListener;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

@Component
public class MyEventHandler{

    @EventListener
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public void handler(MyEvent myEvent) {
        System.out.println("이벤트 받았다. 데이터는 "+ myEvent.getData());
    }
}
package com.bpkim.demospring51;

import org.springframework.context.event.EventListener;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

@Component
public class AnotherHandler {

    @EventListener
    @Order(Ordered.HIGHEST_PRECEDENCE + 2)
    public void handler(MyEvent myEvent) {
        System.out.println("Another "+ myEvent.getData());
    }
}

- 실행결과 
 순서가 바뀌었다.

 

비동기적 이벤트 처리 
 1. EventListener 에 @Async 사용

package com.bpkim.demospring51;

import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

@Component
public class MyEventHandler{

    @EventListener
    @Async
    public void handler(MyEvent myEvent) {
        System.out.println(Thread.currentThread().toString());
        System.out.println("이벤트 받았다. 데이터는 "+ myEvent.getData());
    }
}
package com.bpkim.demospring51;

import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

@Component
public class AnotherHandler {

    @EventListener
    @Async
    public void handler(MyEvent myEvent) {

        System.out.println(Thread.currentThread().toString());
        System.out.println("Another "+ myEvent.getData());
    }
}

 

 2. main 함수가 있는 클래스에 @EnableAsync 선언

package com.bpkim.demospring51;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;

@SpringBootApplication
@EnableAsync
public class Demospring51Application {

    public static void main(String[] args) {

        SpringApplication.run(Demospring51Application.class, args);
    }

}

- 실행결과 
Async 설정 전 : main 스레드에서 실행되고 있다.

Async 설정 후 : 메인 스레드가 아닌 스레드에서 실행되고 있다.

 

스프링이 기본으로 제공하는 이벤트 처리 핸들러 사용 해보기
  - ContextRefreshedEvent : ApplicationContext 초기화 시 발생 
  - ContextStartedEvent : ApplicationContext를 start() 하여 라이프사이클 빈들이 시작신홀르 받은 시점에 발생
  - ContextStoppedEvent : ApplicationContext를 stop() 하여 라이프사이클 빈들이 정지신호를 받은 시점에 발생
  - ContextClosedEvent : ApplicationContext를 close() 하여 싱글톤 빈 소멸되는 시점에 발생
  - RequestHandledEvent : HTTP 요청을 처리했을 때 발생.


1. 핸들러 선언

package com.bpkim.demospring51;

import org.springframework.context.event.*;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

@Component
public class MyEventHandler{

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

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

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

}

실행결과

반응형

'개발 > Spring' 카테고리의 다른 글

Resource 추상화  (0) 2021.04.22
Ioc 컨테이너 - ResourceLoader  (0) 2021.04.22
IoC - MessageSource  (0) 2021.04.11
IoC - Environment - 프로퍼티  (0) 2021.04.11
IoC - Environment - 프로파일  (0) 2021.04.10