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}