001/*
002 * Copyright 2002-2020 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.beans.factory.support;
018
019import java.util.Collections;
020import java.util.HashMap;
021import java.util.HashSet;
022import java.util.Iterator;
023import java.util.LinkedHashMap;
024import java.util.LinkedHashSet;
025import java.util.Map;
026import java.util.Set;
027import java.util.concurrent.ConcurrentHashMap;
028
029import org.springframework.beans.factory.BeanCreationException;
030import org.springframework.beans.factory.BeanCreationNotAllowedException;
031import org.springframework.beans.factory.BeanCurrentlyInCreationException;
032import org.springframework.beans.factory.DisposableBean;
033import org.springframework.beans.factory.ObjectFactory;
034import org.springframework.beans.factory.config.SingletonBeanRegistry;
035import org.springframework.core.SimpleAliasRegistry;
036import org.springframework.lang.Nullable;
037import org.springframework.util.Assert;
038import org.springframework.util.StringUtils;
039
040/**
041 * Generic registry for shared bean instances, implementing the
042 * {@link org.springframework.beans.factory.config.SingletonBeanRegistry}.
043 * Allows for registering singleton instances that should be shared
044 * for all callers of the registry, to be obtained via bean name.
045 *
046 * <p>Also supports registration of
047 * {@link org.springframework.beans.factory.DisposableBean} instances,
048 * (which might or might not correspond to registered singletons),
049 * to be destroyed on shutdown of the registry. Dependencies between
050 * beans can be registered to enforce an appropriate shutdown order.
051 *
052 * <p>This class mainly serves as base class for
053 * {@link org.springframework.beans.factory.BeanFactory} implementations,
054 * factoring out the common management of singleton bean instances. Note that
055 * the {@link org.springframework.beans.factory.config.ConfigurableBeanFactory}
056 * interface extends the {@link SingletonBeanRegistry} interface.
057 *
058 * <p>Note that this class assumes neither a bean definition concept
059 * nor a specific creation process for bean instances, in contrast to
060 * {@link AbstractBeanFactory} and {@link DefaultListableBeanFactory}
061 * (which inherit from it). Can alternatively also be used as a nested
062 * helper to delegate to.
063 *
064 * @author Juergen Hoeller
065 * @since 2.0
066 * @see #registerSingleton
067 * @see #registerDisposableBean
068 * @see org.springframework.beans.factory.DisposableBean
069 * @see org.springframework.beans.factory.config.ConfigurableBeanFactory
070 */
071public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
072
073        /** Maximum number of suppressed exceptions to preserve. */
074        private static final int SUPPRESSED_EXCEPTIONS_LIMIT = 100;
075
076
077        /** Cache of singleton objects: bean name to bean instance. */
078        private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
079
080        /** Cache of singleton factories: bean name to ObjectFactory. */
081        private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
082
083        /** Cache of early singleton objects: bean name to bean instance. */
084        private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);
085
086        /** Set of registered singletons, containing the bean names in registration order. */
087        private final Set<String> registeredSingletons = new LinkedHashSet<>(256);
088
089        /** Names of beans that are currently in creation. */
090        private final Set<String> singletonsCurrentlyInCreation =
091                        Collections.newSetFromMap(new ConcurrentHashMap<>(16));
092
093        /** Names of beans currently excluded from in creation checks. */
094        private final Set<String> inCreationCheckExclusions =
095                        Collections.newSetFromMap(new ConcurrentHashMap<>(16));
096
097        /** Collection of suppressed Exceptions, available for associating related causes. */
098        @Nullable
099        private Set<Exception> suppressedExceptions;
100
101        /** Flag that indicates whether we're currently within destroySingletons. */
102        private boolean singletonsCurrentlyInDestruction = false;
103
104        /** Disposable bean instances: bean name to disposable instance. */
105        private final Map<String, Object> disposableBeans = new LinkedHashMap<>();
106
107        /** Map between containing bean names: bean name to Set of bean names that the bean contains. */
108        private final Map<String, Set<String>> containedBeanMap = new ConcurrentHashMap<>(16);
109
110        /** Map between dependent bean names: bean name to Set of dependent bean names. */
111        private final Map<String, Set<String>> dependentBeanMap = new ConcurrentHashMap<>(64);
112
113        /** Map between depending bean names: bean name to Set of bean names for the bean's dependencies. */
114        private final Map<String, Set<String>> dependenciesForBeanMap = new ConcurrentHashMap<>(64);
115
116
117        @Override
118        public void registerSingleton(String beanName, Object singletonObject) throws IllegalStateException {
119                Assert.notNull(beanName, "Bean name must not be null");
120                Assert.notNull(singletonObject, "Singleton object must not be null");
121                synchronized (this.singletonObjects) {
122                        Object oldObject = this.singletonObjects.get(beanName);
123                        if (oldObject != null) {
124                                throw new IllegalStateException("Could not register object [" + singletonObject +
125                                                "] under bean name '" + beanName + "': there is already object [" + oldObject + "] bound");
126                        }
127                        addSingleton(beanName, singletonObject);
128                }
129        }
130
131        /**
132         * Add the given singleton object to the singleton cache of this factory.
133         * <p>To be called for eager registration of singletons.
134         * @param beanName the name of the bean
135         * @param singletonObject the singleton object
136         */
137        protected void addSingleton(String beanName, Object singletonObject) {
138                synchronized (this.singletonObjects) {
139                        this.singletonObjects.put(beanName, singletonObject);
140                        this.singletonFactories.remove(beanName);
141                        this.earlySingletonObjects.remove(beanName);
142                        this.registeredSingletons.add(beanName);
143                }
144        }
145
146        /**
147         * Add the given singleton factory for building the specified singleton
148         * if necessary.
149         * <p>To be called for eager registration of singletons, e.g. to be able to
150         * resolve circular references.
151         * @param beanName the name of the bean
152         * @param singletonFactory the factory for the singleton object
153         */
154        protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
155                Assert.notNull(singletonFactory, "Singleton factory must not be null");
156                synchronized (this.singletonObjects) {
157                        if (!this.singletonObjects.containsKey(beanName)) {
158                                this.singletonFactories.put(beanName, singletonFactory);
159                                this.earlySingletonObjects.remove(beanName);
160                                this.registeredSingletons.add(beanName);
161                        }
162                }
163        }
164
165        @Override
166        @Nullable
167        public Object getSingleton(String beanName) {
168                return getSingleton(beanName, true);
169        }
170
171        /**
172         * Return the (raw) singleton object registered under the given name.
173         * <p>Checks already instantiated singletons and also allows for an early
174         * reference to a currently created singleton (resolving a circular reference).
175         * @param beanName the name of the bean to look for
176         * @param allowEarlyReference whether early references should be created or not
177         * @return the registered singleton object, or {@code null} if none found
178         */
179        @Nullable
180        protected Object getSingleton(String beanName, boolean allowEarlyReference) {
181                // Quick check for existing instance without full singleton lock
182                Object singletonObject = this.singletonObjects.get(beanName);
183                if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
184                        singletonObject = this.earlySingletonObjects.get(beanName);
185                        if (singletonObject == null && allowEarlyReference) {
186                                synchronized (this.singletonObjects) {
187                                        // Consistent creation of early reference within full singleton lock
188                                        singletonObject = this.singletonObjects.get(beanName);
189                                        if (singletonObject == null) {
190                                                singletonObject = this.earlySingletonObjects.get(beanName);
191                                                if (singletonObject == null) {
192                                                        ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
193                                                        if (singletonFactory != null) {
194                                                                singletonObject = singletonFactory.getObject();
195                                                                this.earlySingletonObjects.put(beanName, singletonObject);
196                                                                this.singletonFactories.remove(beanName);
197                                                        }
198                                                }
199                                        }
200                                }
201                        }
202                }
203                return singletonObject;
204        }
205
206        /**
207         * Return the (raw) singleton object registered under the given name,
208         * creating and registering a new one if none registered yet.
209         * @param beanName the name of the bean
210         * @param singletonFactory the ObjectFactory to lazily create the singleton
211         * with, if necessary
212         * @return the registered singleton object
213         */
214        public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
215                Assert.notNull(beanName, "Bean name must not be null");
216                synchronized (this.singletonObjects) {
217                        Object singletonObject = this.singletonObjects.get(beanName);
218                        if (singletonObject == null) {
219                                if (this.singletonsCurrentlyInDestruction) {
220                                        throw new BeanCreationNotAllowedException(beanName,
221                                                        "Singleton bean creation not allowed while singletons of this factory are in destruction " +
222                                                        "(Do not request a bean from a BeanFactory in a destroy method implementation!)");
223                                }
224                                if (logger.isDebugEnabled()) {
225                                        logger.debug("Creating shared instance of singleton bean '" + beanName + "'");
226                                }
227                                beforeSingletonCreation(beanName);
228                                boolean newSingleton = false;
229                                boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
230                                if (recordSuppressedExceptions) {
231                                        this.suppressedExceptions = new LinkedHashSet<>();
232                                }
233                                try {
234                                        singletonObject = singletonFactory.getObject();
235                                        newSingleton = true;
236                                }
237                                catch (IllegalStateException ex) {
238                                        // Has the singleton object implicitly appeared in the meantime ->
239                                        // if yes, proceed with it since the exception indicates that state.
240                                        singletonObject = this.singletonObjects.get(beanName);
241                                        if (singletonObject == null) {
242                                                throw ex;
243                                        }
244                                }
245                                catch (BeanCreationException ex) {
246                                        if (recordSuppressedExceptions) {
247                                                for (Exception suppressedException : this.suppressedExceptions) {
248                                                        ex.addRelatedCause(suppressedException);
249                                                }
250                                        }
251                                        throw ex;
252                                }
253                                finally {
254                                        if (recordSuppressedExceptions) {
255                                                this.suppressedExceptions = null;
256                                        }
257                                        afterSingletonCreation(beanName);
258                                }
259                                if (newSingleton) {
260                                        addSingleton(beanName, singletonObject);
261                                }
262                        }
263                        return singletonObject;
264                }
265        }
266
267        /**
268         * Register an exception that happened to get suppressed during the creation of a
269         * singleton bean instance, e.g. a temporary circular reference resolution problem.
270         * <p>The default implementation preserves any given exception in this registry's
271         * collection of suppressed exceptions, up to a limit of 100 exceptions, adding
272         * them as related causes to an eventual top-level {@link BeanCreationException}.
273         * @param ex the Exception to register
274         * @see BeanCreationException#getRelatedCauses()
275         */
276        protected void onSuppressedException(Exception ex) {
277                synchronized (this.singletonObjects) {
278                        if (this.suppressedExceptions != null && this.suppressedExceptions.size() < SUPPRESSED_EXCEPTIONS_LIMIT) {
279                                this.suppressedExceptions.add(ex);
280                        }
281                }
282        }
283
284        /**
285         * Remove the bean with the given name from the singleton cache of this factory,
286         * to be able to clean up eager registration of a singleton if creation failed.
287         * @param beanName the name of the bean
288         * @see #getSingletonMutex()
289         */
290        protected void removeSingleton(String beanName) {
291                synchronized (this.singletonObjects) {
292                        this.singletonObjects.remove(beanName);
293                        this.singletonFactories.remove(beanName);
294                        this.earlySingletonObjects.remove(beanName);
295                        this.registeredSingletons.remove(beanName);
296                }
297        }
298
299        @Override
300        public boolean containsSingleton(String beanName) {
301                return this.singletonObjects.containsKey(beanName);
302        }
303
304        @Override
305        public String[] getSingletonNames() {
306                synchronized (this.singletonObjects) {
307                        return StringUtils.toStringArray(this.registeredSingletons);
308                }
309        }
310
311        @Override
312        public int getSingletonCount() {
313                synchronized (this.singletonObjects) {
314                        return this.registeredSingletons.size();
315                }
316        }
317
318
319        public void setCurrentlyInCreation(String beanName, boolean inCreation) {
320                Assert.notNull(beanName, "Bean name must not be null");
321                if (!inCreation) {
322                        this.inCreationCheckExclusions.add(beanName);
323                }
324                else {
325                        this.inCreationCheckExclusions.remove(beanName);
326                }
327        }
328
329        public boolean isCurrentlyInCreation(String beanName) {
330                Assert.notNull(beanName, "Bean name must not be null");
331                return (!this.inCreationCheckExclusions.contains(beanName) && isActuallyInCreation(beanName));
332        }
333
334        protected boolean isActuallyInCreation(String beanName) {
335                return isSingletonCurrentlyInCreation(beanName);
336        }
337
338        /**
339         * Return whether the specified singleton bean is currently in creation
340         * (within the entire factory).
341         * @param beanName the name of the bean
342         */
343        public boolean isSingletonCurrentlyInCreation(String beanName) {
344                return this.singletonsCurrentlyInCreation.contains(beanName);
345        }
346
347        /**
348         * Callback before singleton creation.
349         * <p>The default implementation register the singleton as currently in creation.
350         * @param beanName the name of the singleton about to be created
351         * @see #isSingletonCurrentlyInCreation
352         */
353        protected void beforeSingletonCreation(String beanName) {
354                if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
355                        throw new BeanCurrentlyInCreationException(beanName);
356                }
357        }
358
359        /**
360         * Callback after singleton creation.
361         * <p>The default implementation marks the singleton as not in creation anymore.
362         * @param beanName the name of the singleton that has been created
363         * @see #isSingletonCurrentlyInCreation
364         */
365        protected void afterSingletonCreation(String beanName) {
366                if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.remove(beanName)) {
367                        throw new IllegalStateException("Singleton '" + beanName + "' isn't currently in creation");
368                }
369        }
370
371
372        /**
373         * Add the given bean to the list of disposable beans in this registry.
374         * <p>Disposable beans usually correspond to registered singletons,
375         * matching the bean name but potentially being a different instance
376         * (for example, a DisposableBean adapter for a singleton that does not
377         * naturally implement Spring's DisposableBean interface).
378         * @param beanName the name of the bean
379         * @param bean the bean instance
380         */
381        public void registerDisposableBean(String beanName, DisposableBean bean) {
382                synchronized (this.disposableBeans) {
383                        this.disposableBeans.put(beanName, bean);
384                }
385        }
386
387        /**
388         * Register a containment relationship between two beans,
389         * e.g. between an inner bean and its containing outer bean.
390         * <p>Also registers the containing bean as dependent on the contained bean
391         * in terms of destruction order.
392         * @param containedBeanName the name of the contained (inner) bean
393         * @param containingBeanName the name of the containing (outer) bean
394         * @see #registerDependentBean
395         */
396        public void registerContainedBean(String containedBeanName, String containingBeanName) {
397                synchronized (this.containedBeanMap) {
398                        Set<String> containedBeans =
399                                        this.containedBeanMap.computeIfAbsent(containingBeanName, k -> new LinkedHashSet<>(8));
400                        if (!containedBeans.add(containedBeanName)) {
401                                return;
402                        }
403                }
404                registerDependentBean(containedBeanName, containingBeanName);
405        }
406
407        /**
408         * Register a dependent bean for the given bean,
409         * to be destroyed before the given bean is destroyed.
410         * @param beanName the name of the bean
411         * @param dependentBeanName the name of the dependent bean
412         */
413        public void registerDependentBean(String beanName, String dependentBeanName) {
414                String canonicalName = canonicalName(beanName);
415
416                synchronized (this.dependentBeanMap) {
417                        Set<String> dependentBeans =
418                                        this.dependentBeanMap.computeIfAbsent(canonicalName, k -> new LinkedHashSet<>(8));
419                        if (!dependentBeans.add(dependentBeanName)) {
420                                return;
421                        }
422                }
423
424                synchronized (this.dependenciesForBeanMap) {
425                        Set<String> dependenciesForBean =
426                                        this.dependenciesForBeanMap.computeIfAbsent(dependentBeanName, k -> new LinkedHashSet<>(8));
427                        dependenciesForBean.add(canonicalName);
428                }
429        }
430
431        /**
432         * Determine whether the specified dependent bean has been registered as
433         * dependent on the given bean or on any of its transitive dependencies.
434         * @param beanName the name of the bean to check
435         * @param dependentBeanName the name of the dependent bean
436         * @since 4.0
437         */
438        protected boolean isDependent(String beanName, String dependentBeanName) {
439                synchronized (this.dependentBeanMap) {
440                        return isDependent(beanName, dependentBeanName, null);
441                }
442        }
443
444        private boolean isDependent(String beanName, String dependentBeanName, @Nullable Set<String> alreadySeen) {
445                if (alreadySeen != null && alreadySeen.contains(beanName)) {
446                        return false;
447                }
448                String canonicalName = canonicalName(beanName);
449                Set<String> dependentBeans = this.dependentBeanMap.get(canonicalName);
450                if (dependentBeans == null) {
451                        return false;
452                }
453                if (dependentBeans.contains(dependentBeanName)) {
454                        return true;
455                }
456                for (String transitiveDependency : dependentBeans) {
457                        if (alreadySeen == null) {
458                                alreadySeen = new HashSet<>();
459                        }
460                        alreadySeen.add(beanName);
461                        if (isDependent(transitiveDependency, dependentBeanName, alreadySeen)) {
462                                return true;
463                        }
464                }
465                return false;
466        }
467
468        /**
469         * Determine whether a dependent bean has been registered for the given name.
470         * @param beanName the name of the bean to check
471         */
472        protected boolean hasDependentBean(String beanName) {
473                return this.dependentBeanMap.containsKey(beanName);
474        }
475
476        /**
477         * Return the names of all beans which depend on the specified bean, if any.
478         * @param beanName the name of the bean
479         * @return the array of dependent bean names, or an empty array if none
480         */
481        public String[] getDependentBeans(String beanName) {
482                Set<String> dependentBeans = this.dependentBeanMap.get(beanName);
483                if (dependentBeans == null) {
484                        return new String[0];
485                }
486                synchronized (this.dependentBeanMap) {
487                        return StringUtils.toStringArray(dependentBeans);
488                }
489        }
490
491        /**
492         * Return the names of all beans that the specified bean depends on, if any.
493         * @param beanName the name of the bean
494         * @return the array of names of beans which the bean depends on,
495         * or an empty array if none
496         */
497        public String[] getDependenciesForBean(String beanName) {
498                Set<String> dependenciesForBean = this.dependenciesForBeanMap.get(beanName);
499                if (dependenciesForBean == null) {
500                        return new String[0];
501                }
502                synchronized (this.dependenciesForBeanMap) {
503                        return StringUtils.toStringArray(dependenciesForBean);
504                }
505        }
506
507        public void destroySingletons() {
508                if (logger.isTraceEnabled()) {
509                        logger.trace("Destroying singletons in " + this);
510                }
511                synchronized (this.singletonObjects) {
512                        this.singletonsCurrentlyInDestruction = true;
513                }
514
515                String[] disposableBeanNames;
516                synchronized (this.disposableBeans) {
517                        disposableBeanNames = StringUtils.toStringArray(this.disposableBeans.keySet());
518                }
519                for (int i = disposableBeanNames.length - 1; i >= 0; i--) {
520                        destroySingleton(disposableBeanNames[i]);
521                }
522
523                this.containedBeanMap.clear();
524                this.dependentBeanMap.clear();
525                this.dependenciesForBeanMap.clear();
526
527                clearSingletonCache();
528        }
529
530        /**
531         * Clear all cached singleton instances in this registry.
532         * @since 4.3.15
533         */
534        protected void clearSingletonCache() {
535                synchronized (this.singletonObjects) {
536                        this.singletonObjects.clear();
537                        this.singletonFactories.clear();
538                        this.earlySingletonObjects.clear();
539                        this.registeredSingletons.clear();
540                        this.singletonsCurrentlyInDestruction = false;
541                }
542        }
543
544        /**
545         * Destroy the given bean. Delegates to {@code destroyBean}
546         * if a corresponding disposable bean instance is found.
547         * @param beanName the name of the bean
548         * @see #destroyBean
549         */
550        public void destroySingleton(String beanName) {
551                // Remove a registered singleton of the given name, if any.
552                removeSingleton(beanName);
553
554                // Destroy the corresponding DisposableBean instance.
555                DisposableBean disposableBean;
556                synchronized (this.disposableBeans) {
557                        disposableBean = (DisposableBean) this.disposableBeans.remove(beanName);
558                }
559                destroyBean(beanName, disposableBean);
560        }
561
562        /**
563         * Destroy the given bean. Must destroy beans that depend on the given
564         * bean before the bean itself. Should not throw any exceptions.
565         * @param beanName the name of the bean
566         * @param bean the bean instance to destroy
567         */
568        protected void destroyBean(String beanName, @Nullable DisposableBean bean) {
569                // Trigger destruction of dependent beans first...
570                Set<String> dependencies;
571                synchronized (this.dependentBeanMap) {
572                        // Within full synchronization in order to guarantee a disconnected Set
573                        dependencies = this.dependentBeanMap.remove(beanName);
574                }
575                if (dependencies != null) {
576                        if (logger.isTraceEnabled()) {
577                                logger.trace("Retrieved dependent beans for bean '" + beanName + "': " + dependencies);
578                        }
579                        for (String dependentBeanName : dependencies) {
580                                destroySingleton(dependentBeanName);
581                        }
582                }
583
584                // Actually destroy the bean now...
585                if (bean != null) {
586                        try {
587                                bean.destroy();
588                        }
589                        catch (Throwable ex) {
590                                if (logger.isWarnEnabled()) {
591                                        logger.warn("Destruction of bean with name '" + beanName + "' threw an exception", ex);
592                                }
593                        }
594                }
595
596                // Trigger destruction of contained beans...
597                Set<String> containedBeans;
598                synchronized (this.containedBeanMap) {
599                        // Within full synchronization in order to guarantee a disconnected Set
600                        containedBeans = this.containedBeanMap.remove(beanName);
601                }
602                if (containedBeans != null) {
603                        for (String containedBeanName : containedBeans) {
604                                destroySingleton(containedBeanName);
605                        }
606                }
607
608                // Remove destroyed bean from other beans' dependencies.
609                synchronized (this.dependentBeanMap) {
610                        for (Iterator<Map.Entry<String, Set<String>>> it = this.dependentBeanMap.entrySet().iterator(); it.hasNext();) {
611                                Map.Entry<String, Set<String>> entry = it.next();
612                                Set<String> dependenciesToClean = entry.getValue();
613                                dependenciesToClean.remove(beanName);
614                                if (dependenciesToClean.isEmpty()) {
615                                        it.remove();
616                                }
617                        }
618                }
619
620                // Remove destroyed bean's prepared dependency information.
621                this.dependenciesForBeanMap.remove(beanName);
622        }
623
624        /**
625         * Exposes the singleton mutex to subclasses and external collaborators.
626         * <p>Subclasses should synchronize on the given Object if they perform
627         * any sort of extended singleton creation phase. In particular, subclasses
628         * should <i>not</i> have their own mutexes involved in singleton creation,
629         * to avoid the potential for deadlocks in lazy-init situations.
630         */
631        @Override
632        public final Object getSingletonMutex() {
633                return this.singletonObjects;
634        }
635
636}