001/* 002 * Copyright 2002-2019 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.context.event; 018 019import java.util.ArrayList; 020import java.util.Collection; 021import java.util.LinkedHashSet; 022import java.util.List; 023import java.util.Map; 024import java.util.Set; 025import java.util.concurrent.ConcurrentHashMap; 026 027import org.springframework.aop.framework.AopProxyUtils; 028import org.springframework.beans.factory.BeanClassLoaderAware; 029import org.springframework.beans.factory.BeanFactory; 030import org.springframework.beans.factory.BeanFactoryAware; 031import org.springframework.beans.factory.NoSuchBeanDefinitionException; 032import org.springframework.beans.factory.config.ConfigurableBeanFactory; 033import org.springframework.context.ApplicationEvent; 034import org.springframework.context.ApplicationListener; 035import org.springframework.core.ResolvableType; 036import org.springframework.core.annotation.AnnotationAwareOrderComparator; 037import org.springframework.util.ClassUtils; 038import org.springframework.util.ObjectUtils; 039 040/** 041 * Abstract implementation of the {@link ApplicationEventMulticaster} interface, 042 * providing the basic listener registration facility. 043 * 044 * <p>Doesn't permit multiple instances of the same listener by default, 045 * as it keeps listeners in a linked Set. The collection class used to hold 046 * ApplicationListener objects can be overridden through the "collectionClass" 047 * bean property. 048 * 049 * <p>Implementing ApplicationEventMulticaster's actual {@link #multicastEvent} method 050 * is left to subclasses. {@link SimpleApplicationEventMulticaster} simply multicasts 051 * all events to all registered listeners, invoking them in the calling thread. 052 * Alternative implementations could be more sophisticated in those respects. 053 * 054 * @author Juergen Hoeller 055 * @author Stephane Nicoll 056 * @since 1.2.3 057 * @see #getApplicationListeners(ApplicationEvent, ResolvableType) 058 * @see SimpleApplicationEventMulticaster 059 */ 060public abstract class AbstractApplicationEventMulticaster 061 implements ApplicationEventMulticaster, BeanClassLoaderAware, BeanFactoryAware { 062 063 private final ListenerRetriever defaultRetriever = new ListenerRetriever(false); 064 065 final Map<ListenerCacheKey, ListenerRetriever> retrieverCache = 066 new ConcurrentHashMap<ListenerCacheKey, ListenerRetriever>(64); 067 068 private ClassLoader beanClassLoader; 069 070 private BeanFactory beanFactory; 071 072 private Object retrievalMutex = this.defaultRetriever; 073 074 075 @Override 076 public void setBeanClassLoader(ClassLoader classLoader) { 077 this.beanClassLoader = classLoader; 078 } 079 080 @Override 081 public void setBeanFactory(BeanFactory beanFactory) { 082 this.beanFactory = beanFactory; 083 if (beanFactory instanceof ConfigurableBeanFactory) { 084 ConfigurableBeanFactory cbf = (ConfigurableBeanFactory) beanFactory; 085 if (this.beanClassLoader == null) { 086 this.beanClassLoader = cbf.getBeanClassLoader(); 087 } 088 this.retrievalMutex = cbf.getSingletonMutex(); 089 } 090 } 091 092 private BeanFactory getBeanFactory() { 093 if (this.beanFactory == null) { 094 throw new IllegalStateException("ApplicationEventMulticaster cannot retrieve listener beans " + 095 "because it is not associated with a BeanFactory"); 096 } 097 return this.beanFactory; 098 } 099 100 101 @Override 102 public void addApplicationListener(ApplicationListener<?> listener) { 103 synchronized (this.retrievalMutex) { 104 // Explicitly remove target for a proxy, if registered already, 105 // in order to avoid double invocations of the same listener. 106 Object singletonTarget = AopProxyUtils.getSingletonTarget(listener); 107 if (singletonTarget instanceof ApplicationListener) { 108 this.defaultRetriever.applicationListeners.remove(singletonTarget); 109 } 110 this.defaultRetriever.applicationListeners.add(listener); 111 this.retrieverCache.clear(); 112 } 113 } 114 115 @Override 116 public void addApplicationListenerBean(String listenerBeanName) { 117 synchronized (this.retrievalMutex) { 118 this.defaultRetriever.applicationListenerBeans.add(listenerBeanName); 119 this.retrieverCache.clear(); 120 } 121 } 122 123 @Override 124 public void removeApplicationListener(ApplicationListener<?> listener) { 125 synchronized (this.retrievalMutex) { 126 this.defaultRetriever.applicationListeners.remove(listener); 127 this.retrieverCache.clear(); 128 } 129 } 130 131 @Override 132 public void removeApplicationListenerBean(String listenerBeanName) { 133 synchronized (this.retrievalMutex) { 134 this.defaultRetriever.applicationListenerBeans.remove(listenerBeanName); 135 this.retrieverCache.clear(); 136 } 137 } 138 139 @Override 140 public void removeAllListeners() { 141 synchronized (this.retrievalMutex) { 142 this.defaultRetriever.applicationListeners.clear(); 143 this.defaultRetriever.applicationListenerBeans.clear(); 144 this.retrieverCache.clear(); 145 } 146 } 147 148 149 /** 150 * Return a Collection containing all ApplicationListeners. 151 * @return a Collection of ApplicationListeners 152 * @see org.springframework.context.ApplicationListener 153 */ 154 protected Collection<ApplicationListener<?>> getApplicationListeners() { 155 synchronized (this.retrievalMutex) { 156 return this.defaultRetriever.getApplicationListeners(); 157 } 158 } 159 160 /** 161 * Return a Collection of ApplicationListeners matching the given 162 * event type. Non-matching listeners get excluded early. 163 * @param event the event to be propagated. Allows for excluding 164 * non-matching listeners early, based on cached matching information. 165 * @param eventType the event type 166 * @return a Collection of ApplicationListeners 167 * @see org.springframework.context.ApplicationListener 168 */ 169 protected Collection<ApplicationListener<?>> getApplicationListeners( 170 ApplicationEvent event, ResolvableType eventType) { 171 172 Object source = event.getSource(); 173 Class<?> sourceType = (source != null ? source.getClass() : null); 174 ListenerCacheKey cacheKey = new ListenerCacheKey(eventType, sourceType); 175 176 // Quick check for existing entry on ConcurrentHashMap... 177 ListenerRetriever retriever = this.retrieverCache.get(cacheKey); 178 if (retriever != null) { 179 return retriever.getApplicationListeners(); 180 } 181 182 if (this.beanClassLoader == null || 183 (ClassUtils.isCacheSafe(event.getClass(), this.beanClassLoader) && 184 (sourceType == null || ClassUtils.isCacheSafe(sourceType, this.beanClassLoader)))) { 185 // Fully synchronized building and caching of a ListenerRetriever 186 synchronized (this.retrievalMutex) { 187 retriever = this.retrieverCache.get(cacheKey); 188 if (retriever != null) { 189 return retriever.getApplicationListeners(); 190 } 191 retriever = new ListenerRetriever(true); 192 Collection<ApplicationListener<?>> listeners = 193 retrieveApplicationListeners(eventType, sourceType, retriever); 194 this.retrieverCache.put(cacheKey, retriever); 195 return listeners; 196 } 197 } 198 else { 199 // No ListenerRetriever caching -> no synchronization necessary 200 return retrieveApplicationListeners(eventType, sourceType, null); 201 } 202 } 203 204 /** 205 * Actually retrieve the application listeners for the given event and source type. 206 * @param eventType the event type 207 * @param sourceType the event source type 208 * @param retriever the ListenerRetriever, if supposed to populate one (for caching purposes) 209 * @return the pre-filtered list of application listeners for the given event and source type 210 */ 211 private Collection<ApplicationListener<?>> retrieveApplicationListeners( 212 ResolvableType eventType, Class<?> sourceType, ListenerRetriever retriever) { 213 214 List<ApplicationListener<?>> allListeners = new ArrayList<ApplicationListener<?>>(); 215 Set<ApplicationListener<?>> listeners; 216 Set<String> listenerBeans; 217 synchronized (this.retrievalMutex) { 218 listeners = new LinkedHashSet<ApplicationListener<?>>(this.defaultRetriever.applicationListeners); 219 listenerBeans = new LinkedHashSet<String>(this.defaultRetriever.applicationListenerBeans); 220 } 221 for (ApplicationListener<?> listener : listeners) { 222 if (supportsEvent(listener, eventType, sourceType)) { 223 if (retriever != null) { 224 retriever.applicationListeners.add(listener); 225 } 226 allListeners.add(listener); 227 } 228 } 229 if (!listenerBeans.isEmpty()) { 230 BeanFactory beanFactory = getBeanFactory(); 231 for (String listenerBeanName : listenerBeans) { 232 try { 233 Class<?> listenerType = beanFactory.getType(listenerBeanName); 234 if (listenerType == null || supportsEvent(listenerType, eventType)) { 235 ApplicationListener<?> listener = 236 beanFactory.getBean(listenerBeanName, ApplicationListener.class); 237 if (!allListeners.contains(listener) && supportsEvent(listener, eventType, sourceType)) { 238 if (retriever != null) { 239 retriever.applicationListenerBeans.add(listenerBeanName); 240 } 241 allListeners.add(listener); 242 } 243 } 244 } 245 catch (NoSuchBeanDefinitionException ex) { 246 // Singleton listener instance (without backing bean definition) disappeared - 247 // probably in the middle of the destruction phase 248 } 249 } 250 } 251 AnnotationAwareOrderComparator.sort(allListeners); 252 return allListeners; 253 } 254 255 /** 256 * Filter a listener early through checking its generically declared event 257 * type before trying to instantiate it. 258 * <p>If this method returns {@code true} for a given listener as a first pass, 259 * the listener instance will get retrieved and fully evaluated through a 260 * {@link #supportsEvent(ApplicationListener, ResolvableType, Class)} call afterwards. 261 * @param listenerType the listener's type as determined by the BeanFactory 262 * @param eventType the event type to check 263 * @return whether the given listener should be included in the candidates 264 * for the given event type 265 */ 266 protected boolean supportsEvent(Class<?> listenerType, ResolvableType eventType) { 267 if (GenericApplicationListener.class.isAssignableFrom(listenerType) || 268 SmartApplicationListener.class.isAssignableFrom(listenerType)) { 269 return true; 270 } 271 ResolvableType declaredEventType = GenericApplicationListenerAdapter.resolveDeclaredEventType(listenerType); 272 return (declaredEventType == null || declaredEventType.isAssignableFrom(eventType)); 273 } 274 275 /** 276 * Determine whether the given listener supports the given event. 277 * <p>The default implementation detects the {@link SmartApplicationListener} 278 * and {@link GenericApplicationListener} interfaces. In case of a standard 279 * {@link ApplicationListener}, a {@link GenericApplicationListenerAdapter} 280 * will be used to introspect the generically declared type of the target listener. 281 * @param listener the target listener to check 282 * @param eventType the event type to check against 283 * @param sourceType the source type to check against 284 * @return whether the given listener should be included in the candidates 285 * for the given event type 286 */ 287 protected boolean supportsEvent(ApplicationListener<?> listener, ResolvableType eventType, Class<?> sourceType) { 288 GenericApplicationListener smartListener = (listener instanceof GenericApplicationListener ? 289 (GenericApplicationListener) listener : new GenericApplicationListenerAdapter(listener)); 290 return (smartListener.supportsEventType(eventType) && smartListener.supportsSourceType(sourceType)); 291 } 292 293 294 /** 295 * Cache key for ListenerRetrievers, based on event type and source type. 296 */ 297 private static final class ListenerCacheKey implements Comparable<ListenerCacheKey> { 298 299 private final ResolvableType eventType; 300 301 private final Class<?> sourceType; 302 303 public ListenerCacheKey(ResolvableType eventType, Class<?> sourceType) { 304 this.eventType = eventType; 305 this.sourceType = sourceType; 306 } 307 308 @Override 309 public boolean equals(Object other) { 310 if (this == other) { 311 return true; 312 } 313 ListenerCacheKey otherKey = (ListenerCacheKey) other; 314 return (ObjectUtils.nullSafeEquals(this.eventType, otherKey.eventType) && 315 ObjectUtils.nullSafeEquals(this.sourceType, otherKey.sourceType)); 316 } 317 318 @Override 319 public int hashCode() { 320 return (ObjectUtils.nullSafeHashCode(this.eventType) * 29 + ObjectUtils.nullSafeHashCode(this.sourceType)); 321 } 322 323 @Override 324 public String toString() { 325 return "ListenerCacheKey [eventType = " + this.eventType + ", sourceType = " + this.sourceType.getName() + "]"; 326 } 327 328 @Override 329 public int compareTo(ListenerCacheKey other) { 330 int result = 0; 331 if (this.eventType != null) { 332 result = this.eventType.toString().compareTo(other.eventType.toString()); 333 } 334 if (result == 0 && this.sourceType != null) { 335 result = this.sourceType.getName().compareTo(other.sourceType.getName()); 336 } 337 return result; 338 } 339 } 340 341 342 /** 343 * Helper class that encapsulates a specific set of target listeners, 344 * allowing for efficient retrieval of pre-filtered listeners. 345 * <p>An instance of this helper gets cached per event type and source type. 346 */ 347 private class ListenerRetriever { 348 349 public final Set<ApplicationListener<?>> applicationListeners = new LinkedHashSet<ApplicationListener<?>>(); 350 351 public final Set<String> applicationListenerBeans = new LinkedHashSet<String>(); 352 353 private final boolean preFiltered; 354 355 public ListenerRetriever(boolean preFiltered) { 356 this.preFiltered = preFiltered; 357 } 358 359 public Collection<ApplicationListener<?>> getApplicationListeners() { 360 List<ApplicationListener<?>> allListeners = new ArrayList<ApplicationListener<?>>( 361 this.applicationListeners.size() + this.applicationListenerBeans.size()); 362 allListeners.addAll(this.applicationListeners); 363 if (!this.applicationListenerBeans.isEmpty()) { 364 BeanFactory beanFactory = getBeanFactory(); 365 for (String listenerBeanName : this.applicationListenerBeans) { 366 try { 367 ApplicationListener<?> listener = beanFactory.getBean(listenerBeanName, ApplicationListener.class); 368 if (this.preFiltered || !allListeners.contains(listener)) { 369 allListeners.add(listener); 370 } 371 } 372 catch (NoSuchBeanDefinitionException ex) { 373 // Singleton listener instance (without backing bean definition) disappeared - 374 // probably in the middle of the destruction phase 375 } 376 } 377 } 378 AnnotationAwareOrderComparator.sort(allListeners); 379 return allListeners; 380 } 381 } 382 383}