티스토리 뷰
이번에 다뤄볼 ApplicationEventPublisher도 ApplicationContext의 상위 인터페이스이다.
public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory,
MessageSource, ApplicationEventPublisher, ResourcePatternResolver
ApplicationEventPublisher는 이벤트 프로그래밍을 할 때 유용한 인터페이스이다.
MyEvent 클래스
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;
}
}
MyEventHandler
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
@Component
public class MyEventHandler implements ApplicationListener<MyEvent> {
@Override
public void onApplicationEvent(MyEvent event) {
System.out.println("이벤트 받았습니다. 데이터는 " + event.getData());
}
}
AppRunner
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 publisherEvent;
@Override
public void run(ApplicationArguments args) throws Exception {
publisherEvent.publishEvent(new MyEvent(this, 100));
}
}
위와 같이 코드를 작성한 후에 애플리케이션을 작동시키면 AppRunner 클래스의 run 메소드가 실행이 되어 이벤트가 발생되고(publicshEvent 메소드를 호출하면 된다) 등록되어 있는 bean 중에서 MyEventHandler 클래스를 실행시켜 콘솔에 결과가 출력되게 된다. 하지만 스프링 4.2이후 부터는 이러한 제약사항이 사라졌기 때문에 MyEvent가 ApplicationEvent를 상속받을 필요가 없다.
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;
}
}
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
@Component
public class MyEventHandler {
@EventListener
public void handle(MyEvent event) {
System.out.println(Thread.currentThread().toString());
System.out.println("이벤트 받았습니다. 데이터는 " + event.getData());
}
}
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
@Component
public class AnotherHandler {
@EventListener
public void handle(MyEvent myEvent) {
System.out.println(Thread.currentThread().toString());
System.out.println("Another " + myEvent.getData());
}
}
위와 같은 코드로 ApplicationEvent를 상속받지 않고 더 깔끔하게 @EventListener 어노테이션만을 이용해서 작성할 수 있다. 기본적으로 하나의 Thread에서 synchronized(순차적으로) 실행이 되는데 어떤것이 먼저 실행이 되는지는 잘 모르지만 위에 Thread.currentThread().toString()으로 확인해볼 수 있다.
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 onApplicationEvent(MyEvent event) {
System.out.println(Thread.currentThread().toString());
System.out.println("이벤트 받았습니다. 데이터는 " + event.getData());
}
}
그래서 이번에 순서를 정하고 싶다면 @Order 어노테이션을 이용해서 위와 같이 작성하면 순서를 정할 수 있다.
또 이번에는 비동기적으로 실행을 하고 싶다면 어떻게 해야할까? 비동기는 각각의 스레드에서 따로 실행이 된다.
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;
@SpringBootApplication
@EnableAsync
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
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 onApplicationEvent(MyEvent event) {
System.out.println(Thread.currentThread().toString());
System.out.println("이벤트 받았습니다. 데이터는 " + event.getData());
}
}
MyEventHandler 클래스에서 @Async 어노테이션과 DempApplication에서 @EnableAsync 어노테이션을 추가해주면 각각의 스레드에서 비동기적으로 실행이 된다.
이벤트를 처리하는 방법
- ApplicationListener<이벤트> 구현한 클래스를 만들어서 Bean으로 등록하기
- 스프링 4.2부터는 @EventListener 어노테이션을 빈의 메소드에 사용할 수 있다.
- 기본적으로는 synchronized로 실행이 된다.
- 순서를 정하고 싶다면 @Order와 함께 사용한다.
- 비동기적으로 실행하고 싶다면 @Async와 함께 사용한다.
# 인프런 강의 (백기선 / 스프링 프레임워크 핵심 기술)
IoC 컨테이너 1부: 스프링 IoC 컨테이너와 빈 https://joinwithyou.tistory.com/24
IoC 컨테이너 2부: ApplicationContext와 다양한 빈 설정 방법 https://joinwithyou.tistory.com/25
IoC 컨테이너 3부: @Autowire https://joinwithyou.tistory.com/26
IoC 컨테이너 4부: @Component와 컴포넌트 스캔 https://joinwithyou.tistory.com/27
IoC 컨테이너 5부: 빈의 스코프 https://joinwithyou.tistory.com/28
IoC 컨테이너 6부: Environment 1부. 프로파일 https://joinwithyou.tistory.com/29
IoC 컨테이너 6부: Environment 2부. 프로퍼티 https://joinwithyou.tistory.com/30
IoC 컨테이너 7부: MessageSource https://joinwithyou.tistory.com/31
IoC 컨테이너 8부: ApplicationEventPublisher https://joinwithyou.tistory.com/32
IoC 컨테이너 9부: ResourceLoader https://joinwithyou.tistory.com/33
# Reference
https://devlog-wjdrbs96.tistory.com/165?category=882236 (블로그)
https://www.inflearn.com/course/spring-framework_core (백기선 강의)
'Spring' 카테고리의 다른 글
[Spring] Resource 추상화 (0) | 2021.06.07 |
---|---|
IoC 컨테이너 9부: ResourceLoader (0) | 2021.06.06 |
IoC 컨테이너 7부: MessageSource (0) | 2021.06.06 |
IoC 컨테이너 6부: Environment 2부. 프로퍼티 (0) | 2021.06.06 |
IoC 컨테이너 6부: Environment 1부. 프로파일 (0) | 2021.06.06 |