From 2b0ada95ced0224a8a3f3c649beeb8e22f4bf034 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Mon, 22 Dec 2014 15:46:58 +0100 Subject: [PATCH] AbstractApplicationEventMulticaster populates ListenerRetriever cache in fully synchronized fashion Issue: SPR-12545 --- .../AbstractApplicationEventMulticaster.java | 76 +++++++++++++------ 1 file changed, 53 insertions(+), 23 deletions(-) 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 abb46885f82..6b847f49463 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 @@ -145,43 +145,73 @@ public abstract class AbstractApplicationEventMulticaster Object source = event.getSource(); Class sourceType = (source != null ? source.getClass() : null); ListenerCacheKey cacheKey = new ListenerCacheKey(eventType, sourceType); + + // Quick check for existing entry on ConcurrentHashMap... ListenerRetriever retriever = this.retrieverCache.get(cacheKey); if (retriever != null) { return retriever.getApplicationListeners(); } - else { - retriever = new ListenerRetriever(true); - LinkedList allListeners = new LinkedList(); - Set listeners; - Set listenerBeans; + + if (this.beanClassLoader == null || + (ClassUtils.isCacheSafe(eventType, this.beanClassLoader) && + (sourceType == null || ClassUtils.isCacheSafe(sourceType, this.beanClassLoader)))) { + // Fully synchronized building and caching of a ListenerRetriever synchronized (this.defaultRetriever) { - listeners = new LinkedHashSet(this.defaultRetriever.applicationListeners); - listenerBeans = new LinkedHashSet(this.defaultRetriever.applicationListenerBeans); + retriever = this.retrieverCache.get(cacheKey); + if (retriever != null) { + return retriever.getApplicationListeners(); + } + retriever = new ListenerRetriever(true); + Collection listeners = retrieveApplicationListeners(eventType, sourceType, retriever); + this.retrieverCache.put(cacheKey, retriever); + return listeners; } - for (ApplicationListener listener : listeners) { - if (supportsEvent(listener, eventType, sourceType)) { + } + else { + // No ListenerRetriever caching -> no synchronization necessary + return retrieveApplicationListeners(eventType, sourceType, null); + } + } + + /** + * Actually retrieve the application listeners for the given event and source type. + * @param eventType the application event type + * @param sourceType the event source type + * @param retriever the ListenerRetriever, if supposed to populate one (for caching purposes) + * @return the pre-filtered list of application listeners for the given event and source type + */ + private Collection retrieveApplicationListeners( + Class eventType, Class sourceType, ListenerRetriever retriever) { + + LinkedList allListeners = new LinkedList(); + Set listeners; + Set listenerBeans; + synchronized (this.defaultRetriever) { + listeners = new LinkedHashSet(this.defaultRetriever.applicationListeners); + listenerBeans = new LinkedHashSet(this.defaultRetriever.applicationListenerBeans); + } + for (ApplicationListener listener : listeners) { + if (supportsEvent(listener, eventType, sourceType)) { + if (retriever != null) { retriever.applicationListeners.add(listener); - allListeners.add(listener); } + allListeners.add(listener); } - if (!listenerBeans.isEmpty()) { - BeanFactory beanFactory = getBeanFactory(); - for (String listenerBeanName : listenerBeans) { - ApplicationListener listener = beanFactory.getBean(listenerBeanName, ApplicationListener.class); - if (!allListeners.contains(listener) && supportsEvent(listener, eventType, sourceType)) { + } + if (!listenerBeans.isEmpty()) { + BeanFactory beanFactory = getBeanFactory(); + for (String listenerBeanName : listenerBeans) { + ApplicationListener listener = beanFactory.getBean(listenerBeanName, ApplicationListener.class); + if (!allListeners.contains(listener) && supportsEvent(listener, eventType, sourceType)) { + if (retriever != null) { retriever.applicationListenerBeans.add(listenerBeanName); - allListeners.add(listener); } + allListeners.add(listener); } } - OrderComparator.sort(allListeners); - if (this.beanClassLoader == null || - (ClassUtils.isCacheSafe(eventType, this.beanClassLoader) && - (sourceType == null || ClassUtils.isCacheSafe(sourceType, this.beanClassLoader)))) { - this.retrieverCache.put(cacheKey, retriever); - } - return allListeners; } + OrderComparator.sort(allListeners); + return allListeners; } /**