AbstractApplicationEventMulticaster populates ListenerRetriever cache in fully synchronized fashion

Issue: SPR-12545
(cherry picked from commit 2b0ada9)
This commit is contained in:
Juergen Hoeller 2014-12-22 15:46:58 +01:00
parent c087e51b80
commit 61a6bc0139
1 changed files with 61 additions and 35 deletions

View File

@ -153,53 +153,79 @@ public abstract class AbstractApplicationEventMulticaster
Object source = event.getSource(); Object source = event.getSource();
Class<?> sourceType = (source != null ? source.getClass() : null); Class<?> sourceType = (source != null ? source.getClass() : null);
ListenerCacheKey cacheKey = new ListenerCacheKey(eventType, sourceType); ListenerCacheKey cacheKey = new ListenerCacheKey(eventType, sourceType);
// Quick check for existing entry on ConcurrentHashMap...
ListenerRetriever retriever = this.retrieverCache.get(cacheKey); ListenerRetriever retriever = this.retrieverCache.get(cacheKey);
if (retriever != null) { if (retriever != null) {
return retriever.getApplicationListeners(); return retriever.getApplicationListeners();
} }
else {
retriever = new ListenerRetriever(true); if (this.beanClassLoader == null ||
LinkedList<ApplicationListener<?>> allListeners = new LinkedList<ApplicationListener<?>>(); (ClassUtils.isCacheSafe(eventType, this.beanClassLoader) &&
Set<ApplicationListener<?>> listeners; (sourceType == null || ClassUtils.isCacheSafe(sourceType, this.beanClassLoader)))) {
Set<String> listenerBeans; // Fully synchronized building and caching of a ListenerRetriever
synchronized (this.defaultRetriever) { synchronized (this.defaultRetriever) {
listeners = new LinkedHashSet<ApplicationListener<?>>(this.defaultRetriever.applicationListeners); retriever = this.retrieverCache.get(cacheKey);
listenerBeans = new LinkedHashSet<String>(this.defaultRetriever.applicationListenerBeans); if (retriever != null) {
} return retriever.getApplicationListeners();
for (ApplicationListener<?> listener : listeners) {
if (supportsEvent(listener, eventType, sourceType)) {
retriever.applicationListeners.add(listener);
allListeners.add(listener);
} }
retriever = new ListenerRetriever(true);
Collection<ApplicationListener<?>> listeners = retrieveApplicationListeners(event, sourceType, retriever);
this.retrieverCache.put(cacheKey, retriever);
return listeners;
} }
if (!listenerBeans.isEmpty()) { }
BeanFactory beanFactory = getBeanFactory(); else {
for (String listenerBeanName : listenerBeans) { // No ListenerRetriever caching -> no synchronization necessary
try { return retrieveApplicationListeners(event, sourceType, null);
Class<?> listenerType = beanFactory.getType(listenerBeanName); }
if (listenerType == null || supportsEvent(listenerType, event)) { }
ApplicationListener<?> listener =
beanFactory.getBean(listenerBeanName, ApplicationListener.class); /**
if (!allListeners.contains(listener) && supportsEvent(listener, eventType, sourceType)) { * Actually retrieve the application listeners for the given event and source type.
retriever.applicationListenerBeans.add(listenerBeanName); * @param event the application event
allListeners.add(listener); * @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<ApplicationListener<?>> retrieveApplicationListeners(
ApplicationEvent event, Class<?> sourceType, ListenerRetriever retriever) {
LinkedList<ApplicationListener<?>> allListeners = new LinkedList<ApplicationListener<?>>();
Set<ApplicationListener<?>> listeners;
Set<String> listenerBeans;
synchronized (this.defaultRetriever) {
listeners = new LinkedHashSet<ApplicationListener<?>>(this.defaultRetriever.applicationListeners);
listenerBeans = new LinkedHashSet<String>(this.defaultRetriever.applicationListenerBeans);
}
for (ApplicationListener<?> listener : listeners) {
if (supportsEvent(listener, event.getClass(), sourceType)) {
retriever.applicationListeners.add(listener);
allListeners.add(listener);
}
}
if (!listenerBeans.isEmpty()) {
BeanFactory beanFactory = getBeanFactory();
for (String listenerBeanName : listenerBeans) {
try {
Class<?> listenerType = beanFactory.getType(listenerBeanName);
if (listenerType == null || supportsEvent(listenerType, event)) {
ApplicationListener<?> listener =
beanFactory.getBean(listenerBeanName, ApplicationListener.class);
if (!allListeners.contains(listener) && supportsEvent(listener, event.getClass(), sourceType)) {
retriever.applicationListenerBeans.add(listenerBeanName);
allListeners.add(listener);
} }
} }
catch (NoSuchBeanDefinitionException ex) { }
// Singleton listener instance (without backing bean definition) disappeared - catch (NoSuchBeanDefinitionException ex) {
// probably in the middle of the destruction phase // Singleton listener instance (without backing bean definition) disappeared -
} // probably in the middle of the destruction phase
} }
} }
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;
} }
/** /**