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