| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | ||||
| 4 | 5 | 6 | 7 | 8 | 9 | 10 |
| 11 | 12 | 13 | 14 | 15 | 16 | 17 |
| 18 | 19 | 20 | 21 | 22 | 23 | 24 |
| 25 | 26 | 27 | 28 | 29 | 30 | 31 |
Tags
- c#
- 문법 정리
- 학점
- JVM
- K6
- 오라클 디비
- dynamic query
- 자바 프로젝트
- jsp
- oracle
- 운영체제
- spring
- 파이썬
- auto configure
- hyperledger
- SQL
- 백준 알고리즘
- smart cast
- 리눅스
- 파이썬 소스
- 유사코드
- 오라클
- 자바
- 프로젝트
- gradle
- 초대장
- 티스토리
- 알고리즘
- MongoDB
- resilience4j
Archives
- Today
- Total
모종닷컴
Spring EventListener do not working 본문
반응형
@Component
class Test(...) {
init {
eventPublihser.publishEvent(TestEvent())
}
}
@Service
class TestService {
@EventListener(TestEvent::class)
fun handleEvent(event: TestEvent) {
log.info { "hello" }
}
}이슈
위와 같은 코드를 작성했을 때 애플리케이션이 켜지면 “hello”가 출력되기를 기대했으나 출력이 안됨.
반면 API를 하나 만들어서 동일한 코드를 실행하면 Listener에서 감지가 된다.
원인
/** Class SimpleApplicationEventMulticaster **/
@Override
public void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType) {
ResolvableType type = (eventType != null ? eventType : ResolvableType.forInstance(event));
Executor executor = getTaskExecutor();
for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
if (executor != null && listener.supportsAsyncExecution()) {
try {
executor.execute(() -> invokeListener(listener, event));
}
catch (RejectedExecutionException ex) {
// Probably on shutdown -> invoke listener locally instead
invokeListener(listener, event);
}
}
else {
invokeListener(listener, event);
}
}
}- 애플리케이션 init 에서 publishEvent와 api로 publishEvent 한 것이 어떤 차이가 나는지를 확인해보기 위해 디버깅을 해보면 위 동작이 달라진다.
- 애플리케이션 시작시 publishEvent 호출은 for문의
getApplicationListeners(event, type)결과는 0이고 API로 호출했을 때는 1이 나오면서invoke(listener, event)가 실행된다. - 위 코드는 특별한 로직이 아니다. 이벤트를 처리할 수 있는 리스너를 리턴하면 해당 리스너들에게 이벤트를 전달하는 로직이다.
- init {} 코드가 실행되는 시점에는 리스너가 없었다가 api 호출 시점에는 리스너가 생겼다??
- 여기서 해답을 찾았는데 init이 실행된 타이밍에 TestService의 listener 등록이 늦게 된 것이다.
/** Class AbstractApplicationEventMulticaster **/
@Override
public void addApplicationListener(ApplicationListener<?> listener) {
synchronized (this.defaultRetriever) {
// Explicitly remove target for a proxy, if registered already,
// in order to avoid double invocations of the same listener.
Object singletonTarget = AopProxyUtils.getSingletonTarget(listener);
if (singletonTarget instanceof ApplicationListener) {
this.defaultRetriever.applicationListeners.remove(singletonTarget);
}
this.defaultRetriever.applicationListeners.add(listener);
this.retrieverCache.clear();
}
}- 리스너를 조회하는 코드를 타면 결국 this.defaultRetriever.applicationListeners 로부터 가져오는데 코드 마지막쯤 add코드가 보인다. 이 라인에 브레이크포인트를 걸고 condition을 입력한다.
- listener.getClass() == ApplicationListenerMethodAdapter.class && ((ApplicationListenerMethodAdapter) listener).beanName.equals("")
- 애플리케이션을 실행해보면 init {..} 코드가 먼저 실행되고 이후에 listeners.add 브레이크 포인트가 동작한다.
반응형
'Programming > Spring' 카테고리의 다른 글
| spring boot r2dbc + flyway (0) | 2023.06.24 |
|---|---|
| mysql Insert lock wait timeout 조정해보기 with Spring (1) | 2023.04.23 |
| Mapper 성능 비교 (0) | 2023.02.26 |
| Spring Shell을 이용해 나만의 CLI를 만들어보자 (0) | 2023.01.23 |
| Spring에서 Redis에 동적데이터 저장하기 (2) | 2023.01.14 |