Skip to content

Commit 9b26e4f

Browse files
committed
Avoid potential deadlocks between event multicaster and singleton registry through shared lock
Issue: SPR-12739 (cherry picked from commit 772552b)
1 parent 9a7871f commit 9b26e4f

File tree

2 files changed

+41
-33
lines changed

2 files changed

+41
-33
lines changed

spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultSingletonBeanRegistry.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2013 the original author or authors.
2+
* Copyright 2002-2015 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -317,7 +317,7 @@ public boolean isSingletonCurrentlyInCreation(String beanName) {
317317

318318
/**
319319
* Callback before singleton creation.
320-
* <p>Default implementation register the singleton as currently in creation.
320+
* <p>The default implementation register the singleton as currently in creation.
321321
* @param beanName the name of the singleton about to be created
322322
* @see #isSingletonCurrentlyInCreation
323323
*/
@@ -539,13 +539,13 @@ protected void destroyBean(String beanName, DisposableBean bean) {
539539
}
540540

541541
/**
542-
* Expose the singleton mutex to subclasses.
542+
* Exposes the singleton mutex to subclasses and external collaborators.
543543
* <p>Subclasses should synchronize on the given Object if they perform
544544
* any sort of extended singleton creation phase. In particular, subclasses
545545
* should <i>not</i> have their own mutexes involved in singleton creation,
546546
* to avoid the potential for deadlocks in lazy-init situations.
547547
*/
548-
protected final Object getSingletonMutex() {
548+
public final Object getSingletonMutex() {
549549
return this.singletonObjects;
550550
}
551551

spring-context/src/main/java/org/springframework/context/event/AbstractApplicationEventMulticaster.java

Lines changed: 37 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2014 the original author or authors.
2+
* Copyright 2002-2015 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -27,6 +27,7 @@
2727
import org.springframework.beans.factory.BeanFactory;
2828
import org.springframework.beans.factory.BeanFactoryAware;
2929
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
30+
import org.springframework.beans.factory.support.AbstractBeanFactory;
3031
import org.springframework.context.ApplicationEvent;
3132
import org.springframework.context.ApplicationListener;
3233
import org.springframework.core.OrderComparator;
@@ -64,70 +65,76 @@ public abstract class AbstractApplicationEventMulticaster
6465

6566
private BeanFactory beanFactory;
6667

68+
private Object retrievalMutex = this.defaultRetriever;
69+
70+
71+
public void setBeanClassLoader(ClassLoader classLoader) {
72+
this.beanClassLoader = classLoader;
73+
}
74+
75+
public void setBeanFactory(BeanFactory beanFactory) {
76+
this.beanFactory = beanFactory;
77+
if (this.beanClassLoader == null && beanFactory instanceof ConfigurableBeanFactory) {
78+
this.beanClassLoader = ((ConfigurableBeanFactory) beanFactory).getBeanClassLoader();
79+
}
80+
if (beanFactory instanceof AbstractBeanFactory) {
81+
this.retrievalMutex = ((AbstractBeanFactory) beanFactory).getSingletonMutex();
82+
}
83+
}
84+
85+
private BeanFactory getBeanFactory() {
86+
if (this.beanFactory == null) {
87+
throw new IllegalStateException("ApplicationEventMulticaster cannot retrieve listener beans " +
88+
"because it is not associated with a BeanFactory");
89+
}
90+
return this.beanFactory;
91+
}
92+
6793

6894
public void addApplicationListener(ApplicationListener listener) {
69-
synchronized (this.defaultRetriever) {
95+
synchronized (this.retrievalMutex) {
7096
this.defaultRetriever.applicationListeners.add(listener);
7197
this.retrieverCache.clear();
7298
}
7399
}
74100

75101
public void addApplicationListenerBean(String listenerBeanName) {
76-
synchronized (this.defaultRetriever) {
102+
synchronized (this.retrievalMutex) {
77103
this.defaultRetriever.applicationListenerBeans.add(listenerBeanName);
78104
this.retrieverCache.clear();
79105
}
80106
}
81107

82108
public void removeApplicationListener(ApplicationListener listener) {
83-
synchronized (this.defaultRetriever) {
109+
synchronized (this.retrievalMutex) {
84110
this.defaultRetriever.applicationListeners.remove(listener);
85111
this.retrieverCache.clear();
86112
}
87113
}
88114

89115
public void removeApplicationListenerBean(String listenerBeanName) {
90-
synchronized (this.defaultRetriever) {
116+
synchronized (this.retrievalMutex) {
91117
this.defaultRetriever.applicationListenerBeans.remove(listenerBeanName);
92118
this.retrieverCache.clear();
93119
}
94120
}
95121

96122
public void removeAllListeners() {
97-
synchronized (this.defaultRetriever) {
123+
synchronized (this.retrievalMutex) {
98124
this.defaultRetriever.applicationListeners.clear();
99125
this.defaultRetriever.applicationListenerBeans.clear();
100126
this.retrieverCache.clear();
101127
}
102128
}
103129

104-
public void setBeanClassLoader(ClassLoader classLoader) {
105-
this.beanClassLoader = classLoader;
106-
}
107-
108-
public void setBeanFactory(BeanFactory beanFactory) {
109-
this.beanFactory = beanFactory;
110-
if (this.beanClassLoader == null && beanFactory instanceof ConfigurableBeanFactory) {
111-
this.beanClassLoader = ((ConfigurableBeanFactory) beanFactory).getBeanClassLoader();
112-
}
113-
}
114-
115-
private BeanFactory getBeanFactory() {
116-
if (this.beanFactory == null) {
117-
throw new IllegalStateException("ApplicationEventMulticaster cannot retrieve listener beans " +
118-
"because it is not associated with a BeanFactory");
119-
}
120-
return this.beanFactory;
121-
}
122-
123130

124131
/**
125132
* Return a Collection containing all ApplicationListeners.
126133
* @return a Collection of ApplicationListeners
127134
* @see org.springframework.context.ApplicationListener
128135
*/
129136
protected Collection<ApplicationListener> getApplicationListeners() {
130-
synchronized (this.defaultRetriever) {
137+
synchronized (this.retrievalMutex) {
131138
return this.defaultRetriever.getApplicationListeners();
132139
}
133140
}
@@ -156,13 +163,14 @@ protected Collection<ApplicationListener> getApplicationListeners(ApplicationEve
156163
(ClassUtils.isCacheSafe(eventType, this.beanClassLoader) &&
157164
(sourceType == null || ClassUtils.isCacheSafe(sourceType, this.beanClassLoader)))) {
158165
// Fully synchronized building and caching of a ListenerRetriever
159-
synchronized (this.defaultRetriever) {
166+
synchronized (this.retrievalMutex) {
160167
retriever = this.retrieverCache.get(cacheKey);
161168
if (retriever != null) {
162169
return retriever.getApplicationListeners();
163170
}
164171
retriever = new ListenerRetriever(true);
165-
Collection<ApplicationListener> listeners = retrieveApplicationListeners(eventType, sourceType, retriever);
172+
Collection<ApplicationListener> listeners =
173+
retrieveApplicationListeners(eventType, sourceType, retriever);
166174
this.retrieverCache.put(cacheKey, retriever);
167175
return listeners;
168176
}
@@ -186,7 +194,7 @@ private Collection<ApplicationListener> retrieveApplicationListeners(
186194
LinkedList<ApplicationListener> allListeners = new LinkedList<ApplicationListener>();
187195
Set<ApplicationListener> listeners;
188196
Set<String> listenerBeans;
189-
synchronized (this.defaultRetriever) {
197+
synchronized (this.retrievalMutex) {
190198
listeners = new LinkedHashSet<ApplicationListener>(this.defaultRetriever.applicationListeners);
191199
listenerBeans = new LinkedHashSet<String>(this.defaultRetriever.applicationListenerBeans);
192200
}

0 commit comments

Comments
 (0)