AbstractApplicationEventMulticaster populates ListenerRetriever cache in fully synchronized fashion
Issue: SPR-12545
(cherry picked from commit 2b0ada9
)
This commit is contained in:
parent
c087e51b80
commit
61a6bc0139
|
@ -153,53 +153,79 @@ 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<ApplicationListener<?>> allListeners = new LinkedList<ApplicationListener<?>>();
|
||||
Set<ApplicationListener<?>> listeners;
|
||||
Set<String> 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<ApplicationListener<?>>(this.defaultRetriever.applicationListeners);
|
||||
listenerBeans = new LinkedHashSet<String>(this.defaultRetriever.applicationListenerBeans);
|
||||
}
|
||||
for (ApplicationListener<?> listener : listeners) {
|
||||
if (supportsEvent(listener, eventType, sourceType)) {
|
||||
retriever.applicationListeners.add(listener);
|
||||
allListeners.add(listener);
|
||||
retriever = this.retrieverCache.get(cacheKey);
|
||||
if (retriever != null) {
|
||||
return retriever.getApplicationListeners();
|
||||
}
|
||||
retriever = new ListenerRetriever(true);
|
||||
Collection<ApplicationListener<?>> listeners = retrieveApplicationListeners(event, sourceType, retriever);
|
||||
this.retrieverCache.put(cacheKey, retriever);
|
||||
return listeners;
|
||||
}
|
||||
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, eventType, sourceType)) {
|
||||
retriever.applicationListenerBeans.add(listenerBeanName);
|
||||
allListeners.add(listener);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// No ListenerRetriever caching -> no synchronization necessary
|
||||
return retrieveApplicationListeners(event, sourceType, null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Actually retrieve the application listeners for the given event and source type.
|
||||
* @param event the application event
|
||||
* @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 -
|
||||
// probably in the middle of the destruction phase
|
||||
}
|
||||
}
|
||||
catch (NoSuchBeanDefinitionException ex) {
|
||||
// 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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in New Issue