-
Spring eventSpring 2020. 3. 16. 11:46
https://www.baeldung.com/spring-events
Spring Events | Baeldung
The Basics of Events in Spring - create a simple, custom Event, publish it and handle it in a listener.
www.baeldung.com
스프링 이벤트를 구현하는 방법.
1. 이벤트는 ApplicationEvent 를 상속한다.
2. 퍼블러셔는 ApplicationEventPublisher 를 주입받는다.
3. 리스너는 ApplicationLister interface 를 구현한다.
간단한 event 만들기
public class CustomSpringEvent extends ApplicationEvent { private String message; public CustomSpringEvent(Object source, String message) { super(source); this.message = message; } public String getMessage() { return message; } }
Publisher 만들기
@Component public class CustomSpringEventPublisher { @Autowired private ApplicationEventPublisher applicationEventPublisher; public void doStuffAndPublishAnEvent(final String message) { System.out.println("Publishing custom event. "); CustomSpringEvent customSpringEvent = new CustomSpringEvent(this, message); applicationEventPublisher.publishEvent(customSpringEvent); } }
퍼블리셔는 리스너들이 가져갈 수 있는 이벤트를 생산한다.
위와 같은 방법도 있고, ApplicationEventPublisherAware interface 를 구현하는 것도 가능하다.
하지만 위 방법이 더 간편하다.
Listener 만들기
@Component public class CustomSpringEventListener implements ApplicationListener<CustomSpringEvent> { @Override public void onApplicationEvent(CustomSpringEvent event) { System.out.println("Received spring custom event - " + event.getMessage()); } }
필요한 건 ApplicationListener 를 구현한 것이다.
spring event 는 기본적으로 동기식으로 처리된다. doStuffAndPublishAnEvent 는 이벤트가 처리될 때까지 리스너들을 block 한다.
그렇다면 비동기식으로는 어떻게?
ApplicationEventMuilticaster 를 만들어주고 가능하게 할 수 있다. 이를 통해 SimpleAsyncTaskExecutor 를 이용한다.
@Configuration public class AsynchronousSpringEventsConfig { @Bean(name = "applicationEventMulticaster") public ApplicationEventMulticaster simpleApplicationEventMulticaster() { SimpleApplicationEventMulticaster eventMulticaster = new SimpleApplicationEventMulticaster(); eventMulticaster.setTaskExecutor(new SimpleAsyncTaskExecutor()); return eventMulticaster; } }
설정한 다른 것들은 그대로 두고, 위 설정만 추가한다. 그러면 리스너는 쓰레드와 분리하여 비동기식으로 동작한다.
기존에 스프링에서 기본적으로 존재하는 이벤트들을 리스닝 하고 싶다면?
(ContextRefreshedEvent, ContextStartedEvent, RequestHandledEvent ... etc)
추가적으로 스프링 4.2 버전 이상이라면?
리스너를 어노테이션을 이용하여 적용가능하다.
@Component public class AnnotationDrivenContextStartedListener { // @Async @EventListener public void handleContextStart(ContextStartedEvent cse) { System.out.println("Handling context started event."); } }
굉장히 간편하고, 비동기를 적용하고 싶다면 주석처리해놓은 부분처럼 Async 어노테이션을 이용하면 된다.
제네릭을 이용한 보편적인 이벤트 타입을 만드는 법
public class GenericSpringEvent<T> { private T what; protected boolean success; public GenericSpringEvent(T what, boolean success) { this.what = what; this.success = success; } // ... standard getters }
위와 같이 구성하면 된다. 문서 가장 윗부분의 ApplicationEvent 를 상속받은 이벤트와 차이가 있다.
우리가 유연한 커스텀 이벤트를 만드는 것이기 때문에 상속이 필요하지 않다.
(필요하지 않다?... 보단 상속하지 않고 유연하게 만들겠다 라는 표현이 나을듯)리스너 만들기
@Component public class GenericSpringEventListener implements ApplicationListener<GenericSpringEvent<String>> { @Override public void onApplicationEvent(@NonNull GenericSpringEvent<String> event) { System.out.println("Received spring generic event - " + event.getWhat()); } }
근데 이렇게 하면 GenericSpringEvent 가 ApplicationEvent 를 상속받아야만 한다. 뭐지?
위에서 상속이 필요하지 않다라고 해놓고선...
어노테이션을 이용한 방법을 써서 상속 받지 않고 해결이 가능하댄다..(그럼 왜 가르쳐준거지...)@Component public class AnnotationDrivenEventListener { @EventListener(condition = "#event.success") public void handleSuccessful(GenericSpringEvent<String> event) { System.out.println("Handling generic event (conditional)."); } }
그리고 추가로 SpEL 이라는 expression 을 이용하여, 이벤트 리스너의 조건 설정이 가능하다.
SpEL 에 대해서는 다른 문서에서 다룬다고 한다.
러블리셔의 경우는 위에서 언급한 것과 같지만, 이벤트의 제네릭 부분만 신경써서 해주면 된다.
신경써서 해준다는게 조금 두리뭉실하다. 간단하게 말하면 위의 예제에서는 GenericSpringEvent<String> 을 상속받으면 된다.
또한 이벤트를 collection 에 담아서 여러 이벤트를 들고 다니는 방식도 구현이 가능하다.
트랜잭션 안에서의 이벤트?
스프링 4.2 부터 @TransactionalEventListener 을 이용하여 트랜잭션 안에서의 동작을 정의할 수 있다.
AFTER_COMMIT (default) is used to fire the event if the transaction has completed successfully AFTER_ROLLBACK – if the transaction has rolled back AFTER_COMPLETION – if the transaction has completed (an alias for AFTER_COMMIT and AFTER_ROLLBACK) BEFORE_COMMIT is used to fire the event right before transaction commit
@TransactionalEventListener(phase = TransactionPhase.BEFORE_COMMIT) public void handleCustom(CustomSpringEvent event) { System.out.println("Handling event inside a transaction BEFORE COMMIT."); }
감사합니다.
반응형'Spring' 카테고리의 다른 글
Reactive Spring Boot: Part 1: Kotlin REST Service (0) 2021.01.17 Spring kafka 적용하기 (0) 2020.03.11