1
1
/*
2
- * Copyright 2002-2019 the original author or authors.
2
+ * Copyright 2002-2020 the original author or authors.
3
3
*
4
4
* Licensed under the Apache License, Version 2.0 (the "License");
5
5
* you may not use this file except in compliance with the License.
63
63
public abstract class AbstractApplicationEventMulticaster
64
64
implements ApplicationEventMulticaster , BeanClassLoaderAware , BeanFactoryAware {
65
65
66
- private final ListenerRetriever defaultRetriever = new ListenerRetriever ( false );
66
+ private final DefaultListenerRetriever defaultRetriever = new DefaultListenerRetriever ( );
67
67
68
- final Map <ListenerCacheKey , ListenerRetriever > retrieverCache = new ConcurrentHashMap <>(64 );
68
+ final Map <ListenerCacheKey , CachedListenerRetriever > retrieverCache = new ConcurrentHashMap <>(64 );
69
69
70
70
@ Nullable
71
71
private ClassLoader beanClassLoader ;
72
72
73
73
@ Nullable
74
74
private ConfigurableBeanFactory beanFactory ;
75
75
76
- private Object retrievalMutex = this .defaultRetriever ;
77
-
78
76
79
77
@ Override
80
78
public void setBeanClassLoader (ClassLoader classLoader ) {
@@ -90,7 +88,6 @@ public void setBeanFactory(BeanFactory beanFactory) {
90
88
if (this .beanClassLoader == null ) {
91
89
this .beanClassLoader = this .beanFactory .getBeanClassLoader ();
92
90
}
93
- this .retrievalMutex = this .beanFactory .getSingletonMutex ();
94
91
}
95
92
96
93
private ConfigurableBeanFactory getBeanFactory () {
@@ -104,7 +101,7 @@ private ConfigurableBeanFactory getBeanFactory() {
104
101
105
102
@ Override
106
103
public void addApplicationListener (ApplicationListener <?> listener ) {
107
- synchronized (this .retrievalMutex ) {
104
+ synchronized (this .defaultRetriever ) {
108
105
// Explicitly remove target for a proxy, if registered already,
109
106
// in order to avoid double invocations of the same listener.
110
107
Object singletonTarget = AopProxyUtils .getSingletonTarget (listener );
@@ -118,31 +115,31 @@ public void addApplicationListener(ApplicationListener<?> listener) {
118
115
119
116
@ Override
120
117
public void addApplicationListenerBean (String listenerBeanName ) {
121
- synchronized (this .retrievalMutex ) {
118
+ synchronized (this .defaultRetriever ) {
122
119
this .defaultRetriever .applicationListenerBeans .add (listenerBeanName );
123
120
this .retrieverCache .clear ();
124
121
}
125
122
}
126
123
127
124
@ Override
128
125
public void removeApplicationListener (ApplicationListener <?> listener ) {
129
- synchronized (this .retrievalMutex ) {
126
+ synchronized (this .defaultRetriever ) {
130
127
this .defaultRetriever .applicationListeners .remove (listener );
131
128
this .retrieverCache .clear ();
132
129
}
133
130
}
134
131
135
132
@ Override
136
133
public void removeApplicationListenerBean (String listenerBeanName ) {
137
- synchronized (this .retrievalMutex ) {
134
+ synchronized (this .defaultRetriever ) {
138
135
this .defaultRetriever .applicationListenerBeans .remove (listenerBeanName );
139
136
this .retrieverCache .clear ();
140
137
}
141
138
}
142
139
143
140
@ Override
144
141
public void removeAllListeners () {
145
- synchronized (this .retrievalMutex ) {
142
+ synchronized (this .defaultRetriever ) {
146
143
this .defaultRetriever .applicationListeners .clear ();
147
144
this .defaultRetriever .applicationListenerBeans .clear ();
148
145
this .retrieverCache .clear ();
@@ -156,7 +153,7 @@ public void removeAllListeners() {
156
153
* @see org.springframework.context.ApplicationListener
157
154
*/
158
155
protected Collection <ApplicationListener <?>> getApplicationListeners () {
159
- synchronized (this .retrievalMutex ) {
156
+ synchronized (this .defaultRetriever ) {
160
157
return this .defaultRetriever .getApplicationListeners ();
161
158
}
162
159
}
@@ -177,32 +174,34 @@ protected Collection<ApplicationListener<?>> getApplicationListeners(
177
174
Class <?> sourceType = (source != null ? source .getClass () : null );
178
175
ListenerCacheKey cacheKey = new ListenerCacheKey (eventType , sourceType );
179
176
180
- // Quick check for existing entry on ConcurrentHashMap...
181
- ListenerRetriever retriever = this . retrieverCache . get ( cacheKey ) ;
182
- if ( retriever != null ) {
183
- return retriever . getApplicationListeners ();
184
- }
185
-
186
- if ( this . beanClassLoader == null ||
187
- ( ClassUtils . isCacheSafe ( event . getClass (), this .beanClassLoader ) &&
188
- ( sourceType == null || ClassUtils .isCacheSafe (sourceType , this .beanClassLoader )))) {
189
- // Fully synchronized building and caching of a ListenerRetriever
190
- synchronized ( this . retrievalMutex ) {
191
- retriever = this .retrieverCache .get (cacheKey );
192
- if (retriever != null ) {
193
- return retriever . getApplicationListeners ();
177
+ // Potential new retriever to populate
178
+ CachedListenerRetriever newRetriever = null ;
179
+
180
+ // Quick check for existing entry on ConcurrentHashMap
181
+ CachedListenerRetriever existingRetriever = this . retrieverCache . get ( cacheKey );
182
+ if ( existingRetriever == null ) {
183
+ // Caching a new ListenerRetriever if possible
184
+ if ( this .beanClassLoader == null ||
185
+ ( ClassUtils .isCacheSafe (event . getClass () , this .beanClassLoader ) &&
186
+ ( sourceType == null || ClassUtils . isCacheSafe ( sourceType , this . beanClassLoader )))) {
187
+ newRetriever = new CachedListenerRetriever ();
188
+ existingRetriever = this .retrieverCache .putIfAbsent (cacheKey , newRetriever );
189
+ if (existingRetriever != null ) {
190
+ newRetriever = null ; // no need to populate it in retrieveApplicationListeners
194
191
}
195
- retriever = new ListenerRetriever (true );
196
- Collection <ApplicationListener <?>> listeners =
197
- retrieveApplicationListeners (eventType , sourceType , retriever );
198
- this .retrieverCache .put (cacheKey , retriever );
199
- return listeners ;
200
192
}
201
193
}
202
- else {
203
- // No ListenerRetriever caching -> no synchronization necessary
204
- return retrieveApplicationListeners (eventType , sourceType , null );
194
+
195
+ if (existingRetriever != null ) {
196
+ Collection <ApplicationListener <?>> result = existingRetriever .getApplicationListeners ();
197
+ if (result != null ) {
198
+ return result ;
199
+ }
200
+ // If result is null, the existing retriever is not fully populated yet by another thread.
201
+ // Proceed like caching wasn't possible for this current local attempt.
205
202
}
203
+
204
+ return retrieveApplicationListeners (eventType , sourceType , newRetriever );
206
205
}
207
206
208
207
/**
@@ -213,12 +212,15 @@ protected Collection<ApplicationListener<?>> getApplicationListeners(
213
212
* @return the pre-filtered list of application listeners for the given event and source type
214
213
*/
215
214
private Collection <ApplicationListener <?>> retrieveApplicationListeners (
216
- ResolvableType eventType , @ Nullable Class <?> sourceType , @ Nullable ListenerRetriever retriever ) {
215
+ ResolvableType eventType , @ Nullable Class <?> sourceType , @ Nullable CachedListenerRetriever retriever ) {
217
216
218
217
List <ApplicationListener <?>> allListeners = new ArrayList <>();
218
+ Set <ApplicationListener <?>> filteredListeners = (retriever != null ? new LinkedHashSet <>() : null );
219
+ Set <String > filteredListenerBeans = (retriever != null ? new LinkedHashSet <>() : null );
220
+
219
221
Set <ApplicationListener <?>> listeners ;
220
222
Set <String > listenerBeans ;
221
- synchronized (this .retrievalMutex ) {
223
+ synchronized (this .defaultRetriever ) {
222
224
listeners = new LinkedHashSet <>(this .defaultRetriever .applicationListeners );
223
225
listenerBeans = new LinkedHashSet <>(this .defaultRetriever .applicationListenerBeans );
224
226
}
@@ -228,7 +230,7 @@ private Collection<ApplicationListener<?>> retrieveApplicationListeners(
228
230
for (ApplicationListener <?> listener : listeners ) {
229
231
if (supportsEvent (listener , eventType , sourceType )) {
230
232
if (retriever != null ) {
231
- retriever . applicationListeners .add (listener );
233
+ filteredListeners .add (listener );
232
234
}
233
235
allListeners .add (listener );
234
236
}
@@ -246,10 +248,10 @@ private Collection<ApplicationListener<?>> retrieveApplicationListeners(
246
248
if (!allListeners .contains (listener ) && supportsEvent (listener , eventType , sourceType )) {
247
249
if (retriever != null ) {
248
250
if (beanFactory .isSingleton (listenerBeanName )) {
249
- retriever . applicationListeners .add (listener );
251
+ filteredListeners .add (listener );
250
252
}
251
253
else {
252
- retriever . applicationListenerBeans .add (listenerBeanName );
254
+ filteredListenerBeans .add (listenerBeanName );
253
255
}
254
256
}
255
257
allListeners .add (listener );
@@ -261,7 +263,7 @@ private Collection<ApplicationListener<?>> retrieveApplicationListeners(
261
263
// BeanDefinition metadata (e.g. factory method generics) above.
262
264
Object listener = beanFactory .getSingleton (listenerBeanName );
263
265
if (retriever != null ) {
264
- retriever . applicationListeners .remove (listener );
266
+ filteredListeners .remove (listener );
265
267
}
266
268
allListeners .remove (listener );
267
269
}
@@ -274,9 +276,15 @@ private Collection<ApplicationListener<?>> retrieveApplicationListeners(
274
276
}
275
277
276
278
AnnotationAwareOrderComparator .sort (allListeners );
277
- if (retriever != null && retriever .applicationListenerBeans .isEmpty ()) {
278
- retriever .applicationListeners .clear ();
279
- retriever .applicationListeners .addAll (allListeners );
279
+ if (retriever != null ) {
280
+ if (filteredListenerBeans .isEmpty ()) {
281
+ retriever .applicationListeners = new LinkedHashSet <>(allListeners );
282
+ retriever .applicationListenerBeans = filteredListenerBeans ;
283
+ }
284
+ else {
285
+ retriever .applicationListeners = filteredListeners ;
286
+ retriever .applicationListenerBeans = filteredListenerBeans ;
287
+ }
280
288
}
281
289
return allListeners ;
282
290
}
@@ -415,17 +423,54 @@ public int compareTo(ListenerCacheKey other) {
415
423
* allowing for efficient retrieval of pre-filtered listeners.
416
424
* <p>An instance of this helper gets cached per event type and source type.
417
425
*/
418
- private class ListenerRetriever {
426
+ private class CachedListenerRetriever {
419
427
420
- public final Set <ApplicationListener <?>> applicationListeners = new LinkedHashSet <>();
428
+ @ Nullable
429
+ public volatile Set <ApplicationListener <?>> applicationListeners ;
421
430
422
- public final Set <String > applicationListenerBeans = new LinkedHashSet <>();
431
+ @ Nullable
432
+ public volatile Set <String > applicationListenerBeans ;
423
433
424
- private final boolean preFiltered ;
434
+ @ Nullable
435
+ public Collection <ApplicationListener <?>> getApplicationListeners () {
436
+ Set <ApplicationListener <?>> applicationListeners = this .applicationListeners ;
437
+ Set <String > applicationListenerBeans = this .applicationListenerBeans ;
438
+ if (applicationListeners == null || applicationListenerBeans == null ) {
439
+ // Not fully populated yet
440
+ return null ;
441
+ }
425
442
426
- public ListenerRetriever (boolean preFiltered ) {
427
- this .preFiltered = preFiltered ;
443
+ List <ApplicationListener <?>> allListeners = new ArrayList <>(
444
+ applicationListeners .size () + applicationListenerBeans .size ());
445
+ allListeners .addAll (applicationListeners );
446
+ if (!applicationListenerBeans .isEmpty ()) {
447
+ BeanFactory beanFactory = getBeanFactory ();
448
+ for (String listenerBeanName : applicationListenerBeans ) {
449
+ try {
450
+ allListeners .add (beanFactory .getBean (listenerBeanName , ApplicationListener .class ));
451
+ }
452
+ catch (NoSuchBeanDefinitionException ex ) {
453
+ // Singleton listener instance (without backing bean definition) disappeared -
454
+ // probably in the middle of the destruction phase
455
+ }
456
+ }
457
+ }
458
+ if (!applicationListenerBeans .isEmpty ()) {
459
+ AnnotationAwareOrderComparator .sort (allListeners );
460
+ }
461
+ return allListeners ;
428
462
}
463
+ }
464
+
465
+
466
+ /**
467
+ * Helper class that encapsulates a general set of target listeners.
468
+ */
469
+ private class DefaultListenerRetriever {
470
+
471
+ public final Set <ApplicationListener <?>> applicationListeners = new LinkedHashSet <>();
472
+
473
+ public final Set <String > applicationListenerBeans = new LinkedHashSet <>();
429
474
430
475
public Collection <ApplicationListener <?>> getApplicationListeners () {
431
476
List <ApplicationListener <?>> allListeners = new ArrayList <>(
@@ -435,8 +480,9 @@ public Collection<ApplicationListener<?>> getApplicationListeners() {
435
480
BeanFactory beanFactory = getBeanFactory ();
436
481
for (String listenerBeanName : this .applicationListenerBeans ) {
437
482
try {
438
- ApplicationListener <?> listener = beanFactory .getBean (listenerBeanName , ApplicationListener .class );
439
- if (this .preFiltered || !allListeners .contains (listener )) {
483
+ ApplicationListener <?> listener =
484
+ beanFactory .getBean (listenerBeanName , ApplicationListener .class );
485
+ if (!allListeners .contains (listener )) {
440
486
allListeners .add (listener );
441
487
}
442
488
}
@@ -446,9 +492,7 @@ public Collection<ApplicationListener<?>> getApplicationListeners() {
446
492
}
447
493
}
448
494
}
449
- if (!this .preFiltered || !this .applicationListenerBeans .isEmpty ()) {
450
- AnnotationAwareOrderComparator .sort (allListeners );
451
- }
495
+ AnnotationAwareOrderComparator .sort (allListeners );
452
496
return allListeners ;
453
497
}
454
498
}
0 commit comments