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}