(Spring) EventListener
https://www.baeldung.com/spring-events
ApplicationEventPublisher::publishEvent
로 pub 하고@EventListener
로 event 받아 처리하면 된다.- 단 여기서 주의해야 할점! listener가 이벤트를 수신하는 것이 왠지 비동기로 이루어질 것 같지만, 기본적으로 동기식이다!
- 한 스레드가 pub한 다음, 해당 이벤트를 처리해야 하는 EventListener들을 돌면서 동기식으로 직접 리스너를 수행한다.
- By default, the listener is invoked synchronously. However, we can easily make it asynchronous by adding an @Async annotation. We just need to remember toenableAsyncsupportin the application.
Event와 순환 참조 문제
- Event를 사용하면 의존성을 끊을 수 있지만, 이렇게 의존성을 끊고 나서 반대방향 참조를 하게 되면 아래와 같은 에러를 마주칠 수 있다.
1
2
3
22:10:41.056 [WebSocketClient-SecureIO-2] ERROR o.s.w.s.a.s.StandardWebSocketHandlerAdapter - Closing session due to exception for StandardWebSocketSession[id=b85eba15-6fc8-6b1f-1c6d-bb93a2ccb1e7, uri=null]
org.springframework.beans.factory.BeanCreationNotAllowedException:
Error creating bean with name 'myContainer': Singleton bean creation not allowed while singletons of this factory are in destruction (Do not request a bean from a BeanFactory in a destroy method implementation!)
원인?
- 종료 시, Listener가 Publisher를 참조하고 있으므로, Listener bean이 먼저 destroy 된다.
- Publisher가 Event를 publish하면, 이벤트를 수신할 bean을 가져오게 되는데, 이 때 최종적으로
DefaultSingletonBeanRegistry.getSingleton
를 사용하여 가져오게 된다. 이 메서드는 싱글턴을 반환하는데 없으면 생성 시도한다. - Listener는 이미 destroy 되었으므로 생성 시도할텐데, shutdown으로 destruction phase에 진입했기 때문에 새로운 빈을 생성할 수 없다. 따라서 에러 발생하는 것.
1
2
3
4
5
6
7
8
9
10
11
/**
* Return the (raw) singleton object registered under the given name,
* creating and registering a new one if none registered yet.
*/
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(beanName, "Bean name must not be null");
synchronized (this.singletonObjects) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
if (this.singletonsCurrentlyInDestruction) {
throw new BeanCreationNotAllowedException(beanName, ...
해결방안?
- 무시하거나, (어차피 종료 시에만 뜬다.)
- try-catch 처리하거나,
- application이 종료될 때, shutdown hook을 이용해서 flag 변수를 set하고 이를 체크해서 publish하는 식으로 동작하거나. (✓)
도메인 모델에서 이벤트 직접 발행
- https://namjug-kim.github.io/2020/03/24/spring-ddd-domain-event.html
- https://www.baeldung.com/spring-data-ddd
Event는 언제 유용한가?
- 가격 조회 local container가 갱신 될 때 마다 특정 작업을 호출해야 하는데, 가격 조회 관련 기능이 별도 client module로 분리되어 있는 경우.
- 모듈 참조 방향 때문에 client module에서는 갱신 시 호출 될 작업들에 대해서는 알 수 없다.
- 이런 경우 client module에서는 event 발행하고, 나머지 비즈니스 모듈에서 event 구독하는 방식으로 처리하게 됨.
ApplicationEventMulticaster 로 비동기 push
https://mangkyu.tistory.com/292
This post is licensed under CC BY 4.0 by the author.