From 20c688e68de2fe9db9dda95bb1b43c6147ae0007 Mon Sep 17 00:00:00 2001 From: Alexander Kriegisch Date: Mon, 11 Apr 2022 14:51:04 +0700 Subject: [PATCH] Avoid duplicate application listeners (proxy vs. proxy target) In AbstractApplicationEventMulticaster.retrieveApplicationListeners, despite best efforts to avoid it, unwrapped proxies (singleton targets) can end up in the list of programmatically registered listeners. In order to avoid duplicates, we need to find and replace them by their proxy counterparts, because if both a proxy and its target end up in 'allListeners', listeners will fire twice. Fixes #28283. --- .../AbstractApplicationEventMulticaster.java | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/spring-context/src/main/java/org/springframework/context/event/AbstractApplicationEventMulticaster.java b/spring-context/src/main/java/org/springframework/context/event/AbstractApplicationEventMulticaster.java index f22e0ceb721..76be0c05252 100644 --- a/spring-context/src/main/java/org/springframework/context/event/AbstractApplicationEventMulticaster.java +++ b/spring-context/src/main/java/org/springframework/context/event/AbstractApplicationEventMulticaster.java @@ -263,6 +263,24 @@ public abstract class AbstractApplicationEventMulticaster if (supportsEvent(beanFactory, listenerBeanName, eventType)) { ApplicationListener listener = beanFactory.getBean(listenerBeanName, ApplicationListener.class); + + // Despite best efforts to avoid it, unwrapped proxies (singleton targets) can end up in the + // list of programmatically registered listeners. In order to avoid duplicates, we need to find + // and replace them by their proxy counterparts, because if both a proxy and its target end up + // in 'allListeners', listeners will fire twice. + ApplicationListener unwrappedListener = + (ApplicationListener) AopProxyUtils.getSingletonTarget(listener); + if (listener != unwrappedListener) { + if (filteredListeners != null && filteredListeners.contains(unwrappedListener)) { + filteredListeners.remove(unwrappedListener); + filteredListeners.add(listener); + } + if (allListeners.contains(unwrappedListener)) { + allListeners.remove(unwrappedListener); + allListeners.add(listener); + } + } + if (!allListeners.contains(listener) && supportsEvent(listener, eventType, sourceType)) { if (retriever != null) { if (beanFactory.isSingleton(listenerBeanName)) {