Save This Page
Home » spring-framework-2.5.6-with-dependencies » org.springframework » beans » factory » support » [javadoc | source]
    1   /*
    2    * Copyright 2002-2008 the original author or authors.
    3    *
    4    * Licensed under the Apache License, Version 2.0 (the "License");
    5    * you may not use this file except in compliance with the License.
    6    * You may obtain a copy of the License at
    7    *
    8    *      http://www.apache.org/licenses/LICENSE-2.0
    9    *
   10    * Unless required by applicable law or agreed to in writing, software
   11    * distributed under the License is distributed on an "AS IS" BASIS,
   12    * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   13    * See the License for the specific language governing permissions and
   14    * limitations under the License.
   15    */
   16   
   17   package org.springframework.beans.factory.support;
   18   
   19   import java.util.Collections;
   20   import java.util.HashMap;
   21   import java.util.HashSet;
   22   import java.util.Iterator;
   23   import java.util.LinkedHashMap;
   24   import java.util.LinkedHashSet;
   25   import java.util.Map;
   26   import java.util.Set;
   27   
   28   import org.apache.commons.logging.Log;
   29   import org.apache.commons.logging.LogFactory;
   30   
   31   import org.springframework.beans.factory.BeanCreationException;
   32   import org.springframework.beans.factory.BeanCreationNotAllowedException;
   33   import org.springframework.beans.factory.BeanCurrentlyInCreationException;
   34   import org.springframework.beans.factory.DisposableBean;
   35   import org.springframework.beans.factory.ObjectFactory;
   36   import org.springframework.beans.factory.config.SingletonBeanRegistry;
   37   import org.springframework.core.CollectionFactory;
   38   import org.springframework.core.SimpleAliasRegistry;
   39   import org.springframework.util.Assert;
   40   import org.springframework.util.StringUtils;
   41   
   42   /**
   43    * Generic registry for shared bean instances, implementing the
   44    * {@link org.springframework.beans.factory.config.SingletonBeanRegistry}.
   45    * Allows for registering singleton instances that should be shared
   46    * for all callers of the registry, to be obtained via bean name.
   47    *
   48    * <p>Also supports registration of
   49    * {@link org.springframework.beans.factory.DisposableBean} instances,
   50    * (which might or might not correspond to registered singletons),
   51    * to be destroyed on shutdown of the registry. Dependencies between
   52    * beans can be registered to enforce an appropriate shutdown order.
   53    *
   54    * <p>This class mainly serves as base class for
   55    * {@link org.springframework.beans.factory.BeanFactory} implementations,
   56    * factoring out the common management of singleton bean instances. Note that
   57    * the {@link org.springframework.beans.factory.config.ConfigurableBeanFactory}
   58    * interface extends the {@link SingletonBeanRegistry} interface.
   59    *
   60    * <p>Note that this class assumes neither a bean definition concept
   61    * nor a specific creation process for bean instances, in contrast to
   62    * {@link AbstractBeanFactory} and {@link DefaultListableBeanFactory}
   63    * (which inherit from it). Can alternatively also be used as a nested
   64    * helper to delegate to.
   65    *
   66    * @author Juergen Hoeller
   67    * @since 2.0
   68    * @see #registerSingleton
   69    * @see #registerDisposableBean
   70    * @see org.springframework.beans.factory.DisposableBean
   71    * @see org.springframework.beans.factory.config.ConfigurableBeanFactory
   72    */
   73   public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
   74   
   75   	/**
   76   	 * Internal marker for a null singleton object:
   77   	 * used as marker value for concurrent Maps (which don't support null values).
   78   	 */
   79   	protected static final Object NULL_OBJECT = new Object();
   80   
   81   
   82   	/** Logger available to subclasses */
   83   	protected final Log logger = LogFactory.getLog(getClass());
   84   
   85   	/** Cache of singleton objects: bean name --> bean instance */
   86   	private final Map singletonObjects = CollectionFactory.createConcurrentMapIfPossible(16);
   87   
   88   	/** Cache of singleton factories: bean name --> ObjectFactory */
   89   	private final Map singletonFactories = new HashMap();
   90   
   91   	/** Cache of early singleton objects: bean name --> bean instance */
   92   	private final Map earlySingletonObjects = new HashMap();
   93   
   94   	/** Set of registered singletons, containing the bean names in registration order */
   95   	private final Set registeredSingletons = new LinkedHashSet(16);
   96   
   97   	/** Names of beans that are currently in creation */
   98   	private final Set singletonsCurrentlyInCreation = Collections.synchronizedSet(new HashSet());
   99   
  100   	/** List of suppressed Exceptions, available for associating related causes */
  101   	private Set suppressedExceptions;
  102   
  103   	/** Flag that indicates whether we're currently within destroySingletons */
  104   	private boolean singletonsCurrentlyInDestruction = false;
  105   
  106   	/** Disposable bean instances: bean name --> disposable instance */
  107   	private final Map disposableBeans = new LinkedHashMap(16);
  108   
  109   	/** Map between containing bean names: bean name --> Set of bean names that the bean contains */
  110   	private final Map containedBeanMap = CollectionFactory.createConcurrentMapIfPossible(16);
  111   
  112   	/** Map between dependent bean names: bean name --> Set of dependent bean names */
  113   	private final Map dependentBeanMap = CollectionFactory.createConcurrentMapIfPossible(16);
  114   
  115   	/** Map between depending bean names: bean name --> Set of bean names for the bean's dependencies */
  116   	private final Map dependenciesForBeanMap = CollectionFactory.createConcurrentMapIfPossible(16);
  117   
  118   
  119   	public void registerSingleton(String beanName, Object singletonObject) throws IllegalStateException {
  120   		Assert.notNull(beanName, "'beanName' 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 != null ? singletonObject : NULL_OBJECT));
  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   	public Object getSingleton(String beanName) {
  166   		return getSingleton(beanName, true);
  167   	}
  168   
  169   	/**
  170   	 * Return the (raw) singleton object registered under the given name.
  171   	 * <p>Checks already instantiated singletons and also allows for an early
  172   	 * reference to a currently created singleton (resolving a circular reference).
  173   	 * @param beanName the name of the bean to look for
  174   	 * @param allowEarlyReference whether early references should be created or not
  175   	 * @return the registered singleton object, or <code>null</code> if none found
  176   	 */
  177   	protected Object getSingleton(String beanName, boolean allowEarlyReference) {
  178   		Object singletonObject = this.singletonObjects.get(beanName);
  179   		if (singletonObject == null) {
  180   			synchronized (this.singletonObjects) {
  181   				singletonObject = this.earlySingletonObjects.get(beanName);
  182   				if (singletonObject == null && allowEarlyReference) {
  183   					ObjectFactory singletonFactory = (ObjectFactory) this.singletonFactories.get(beanName);
  184   					if (singletonFactory != null) {
  185   						singletonObject = singletonFactory.getObject();
  186   						this.earlySingletonObjects.put(beanName, singletonObject);
  187   						this.singletonFactories.remove(beanName);
  188   					}
  189   				}
  190   			}
  191   		}
  192   		return (singletonObject != NULL_OBJECT ? singletonObject : null);
  193   	}
  194   
  195   	/**
  196   	 * Return the (raw) singleton object registered under the given name,
  197   	 * creating and registering a new one if none registered yet.
  198   	 * @param beanName the name of the bean
  199   	 * @param singletonFactory the ObjectFactory to lazily create the singleton
  200   	 * with, if necessary
  201   	 * @return the registered singleton object
  202   	 */
  203   	public Object getSingleton(String beanName, ObjectFactory singletonFactory) {
  204   		Assert.notNull(beanName, "'beanName' must not be null");
  205   		synchronized (this.singletonObjects) {
  206   			Object singletonObject = this.singletonObjects.get(beanName);
  207   			if (singletonObject == null) {
  208   				if (this.singletonsCurrentlyInDestruction) {
  209   					throw new BeanCreationNotAllowedException(beanName,
  210   							"Singleton bean creation not allowed while the singletons of this factory are in destruction " +
  211   							"(Do not request a bean from a BeanFactory in a destroy method implementation!)");
  212   				}
  213   				if (logger.isDebugEnabled()) {
  214   					logger.debug("Creating shared instance of singleton bean '" + beanName + "'");
  215   				}
  216   				beforeSingletonCreation(beanName);
  217   				boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
  218   				if (recordSuppressedExceptions) {
  219   					this.suppressedExceptions = new LinkedHashSet();
  220   				}
  221   				try {
  222   					singletonObject = singletonFactory.getObject();
  223   				}
  224   				catch (BeanCreationException ex) {
  225   					if (recordSuppressedExceptions) {
  226   						for (Iterator it = this.suppressedExceptions.iterator(); it.hasNext();) {
  227   							ex.addRelatedCause((Exception) it.next());
  228   						}
  229   					}
  230   					throw ex;
  231   				}
  232   				finally {
  233   					if (recordSuppressedExceptions) {
  234   						this.suppressedExceptions = null;
  235   					}
  236   					afterSingletonCreation(beanName);
  237   				}
  238   				addSingleton(beanName, singletonObject);
  239   			}
  240   			return (singletonObject != NULL_OBJECT ? singletonObject : null);
  241   		}
  242   	}
  243   
  244   	/**
  245   	 * Register an Exception that happened to get suppressed during the creation of a
  246   	 * singleton bean instance, e.g. a temporary circular reference resolution problem.
  247   	 * @param ex the Exception to register
  248   	 */
  249   	protected void onSuppressedException(Exception ex) {
  250   		synchronized (this.singletonObjects) {
  251   			if (this.suppressedExceptions != null) {
  252   				this.suppressedExceptions.add(ex);
  253   			}
  254   		}
  255   	}
  256   
  257   	/**
  258   	 * Remove the bean with the given name from the singleton cache of this factory,
  259   	 * to be able to clean up eager registration of a singleton if creation failed.
  260   	 * @param beanName the name of the bean
  261   	 * @see #getSingletonMutex()
  262   	 */
  263   	protected void removeSingleton(String beanName) {
  264   		synchronized (this.singletonObjects) {
  265   			this.singletonObjects.remove(beanName);
  266   			this.singletonFactories.remove(beanName);
  267   			this.earlySingletonObjects.remove(beanName);
  268   			this.registeredSingletons.remove(beanName);
  269   		}
  270   	}
  271   
  272   	public boolean containsSingleton(String beanName) {
  273   		return (this.singletonObjects.containsKey(beanName));
  274   	}
  275   
  276   	public String[] getSingletonNames() {
  277   		synchronized (this.singletonObjects) {
  278   			return StringUtils.toStringArray(this.registeredSingletons);
  279   		}
  280   	}
  281   
  282   	public int getSingletonCount() {
  283   		synchronized (this.singletonObjects) {
  284   			return this.registeredSingletons.size();
  285   		}
  286   	}
  287   
  288   
  289   	/**
  290   	 * Callback before singleton creation.
  291   	 * <p>Default implementation register the singleton as currently in creation.
  292   	 * @param beanName the name of the singleton about to be created
  293   	 * @see #isSingletonCurrentlyInCreation
  294   	 */
  295   	protected void beforeSingletonCreation(String beanName) {
  296   		if (!this.singletonsCurrentlyInCreation.add(beanName)) {
  297   			throw new BeanCurrentlyInCreationException(beanName);
  298   		}
  299   	}
  300   
  301   	/**
  302   	 * Callback after singleton creation.
  303   	 * <p>Default implementation marks the singleton as not in creation anymore.
  304   	 * @param beanName the name of the singleton that has been created
  305   	 * @see #isSingletonCurrentlyInCreation
  306   	 */
  307   	protected void afterSingletonCreation(String beanName) {
  308   		if (!this.singletonsCurrentlyInCreation.remove(beanName)) {
  309   			throw new IllegalStateException("Singleton '" + beanName + "' isn't currently in creation");
  310   		}
  311   	}
  312   
  313   	/**
  314   	 * Return whether the specified singleton bean is currently in creation
  315   	 * (within the entire factory).
  316   	 * @param beanName the name of the bean
  317   	 */
  318   	public final boolean isSingletonCurrentlyInCreation(String beanName) {
  319   		return this.singletonsCurrentlyInCreation.contains(beanName);
  320   	}
  321   
  322   
  323   	/**
  324   	 * Add the given bean to the list of disposable beans in this registry.
  325   	 * Disposable beans usually correspond to registered singletons,
  326   	 * matching the bean name but potentially being a different instance
  327   	 * (for example, a DisposableBean adapter for a singleton that does not
  328   	 * naturally implement Spring's DisposableBean interface).
  329   	 * @param beanName the name of the bean
  330   	 * @param bean the bean instance
  331   	 */
  332   	public void registerDisposableBean(String beanName, DisposableBean bean) {
  333   		synchronized (this.disposableBeans) {
  334   			this.disposableBeans.put(beanName, bean);
  335   		}
  336   	}
  337   
  338   	/**
  339   	 * Register a containment relationship between two beans,
  340   	 * e.g. between an inner bean and its containing outer bean.
  341   	 * <p>Also registers the containing bean as dependent on the contained bean
  342   	 * in terms of destruction order.
  343   	 * @param containedBeanName the name of the contained (inner) bean
  344   	 * @param containingBeanName the name of the containing (outer) bean
  345   	 * @see #registerDependentBean
  346   	 */
  347   	public void registerContainedBean(String containedBeanName, String containingBeanName) {
  348   		synchronized (this.containedBeanMap) {
  349   			Set containedBeans = (Set) this.containedBeanMap.get(containingBeanName);
  350   			if (containedBeans == null) {
  351   				containedBeans = new LinkedHashSet(8);
  352   				this.containedBeanMap.put(containingBeanName, containedBeans);
  353   			}
  354   			containedBeans.add(containedBeanName);
  355   		}
  356   		registerDependentBean(containedBeanName, containingBeanName);
  357   	}
  358   
  359   	/**
  360   	 * Register a dependent bean for the given bean,
  361   	 * to be destroyed before the given bean is destroyed.
  362   	 * @param beanName the name of the bean
  363   	 * @param dependentBeanName the name of the dependent bean
  364   	 */
  365   	public void registerDependentBean(String beanName, String dependentBeanName) {
  366   		synchronized (this.dependentBeanMap) {
  367   			Set dependentBeans = (Set) this.dependentBeanMap.get(beanName);
  368   			if (dependentBeans == null) {
  369   				dependentBeans = new LinkedHashSet(8);
  370   				this.dependentBeanMap.put(beanName, dependentBeans);
  371   			}
  372   			dependentBeans.add(dependentBeanName);
  373   		}
  374   		synchronized (this.dependenciesForBeanMap) {
  375   			Set dependenciesForBean = (Set) this.dependenciesForBeanMap.get(dependentBeanName);
  376   			if (dependenciesForBean == null) {
  377   				dependenciesForBean = new LinkedHashSet(8);
  378   				this.dependenciesForBeanMap.put(dependentBeanName, dependenciesForBean);
  379   			}
  380   			dependenciesForBean.add(beanName);
  381   		}
  382   	}
  383   
  384   	/**
  385   	 * Determine whether a dependent bean has been registered for the given name.
  386   	 * @param beanName the name of the bean to check
  387   	 */
  388   	protected boolean hasDependentBean(String beanName) {
  389   		return this.dependentBeanMap.containsKey(beanName);
  390   	}
  391   
  392   	/**
  393   	 * Return the names of all beans which depend on the specified bean, if any.
  394   	 * @param beanName the name of the bean
  395   	 * @return the array of dependent bean names, or an empty array if none
  396   	 */
  397   	public String[] getDependentBeans(String beanName) {
  398   		Set dependentBeans = (Set) this.dependentBeanMap.get(beanName);
  399   		if (dependentBeans == null) {
  400   			return new String[0];
  401   		}
  402   		return (String[]) dependentBeans.toArray(new String[dependentBeans.size()]);
  403   	}
  404   
  405   	/**
  406   	 * Return the names of all beans that the specified bean depends on, if any.
  407   	 * @param beanName the name of the bean
  408   	 * @return the array of names of beans which the bean depends on,
  409   	 * or an empty array if none
  410   	 */
  411   	public String[] getDependenciesForBean(String beanName) {
  412   		Set dependenciesForBean = (Set) this.dependenciesForBeanMap.get(beanName);
  413   		if (dependenciesForBean == null) {
  414   			return new String[0];
  415   		}
  416   		return (String[]) dependenciesForBean.toArray(new String[dependenciesForBean.size()]);
  417   	}
  418   
  419   	public void destroySingletons() {
  420   		if (logger.isInfoEnabled()) {
  421   			logger.info("Destroying singletons in " + this);
  422   		}
  423   		synchronized (this.singletonObjects) {
  424   			this.singletonsCurrentlyInDestruction = true;
  425   		}
  426   
  427   		synchronized (this.disposableBeans) {
  428   			String[] disposableBeanNames = StringUtils.toStringArray(this.disposableBeans.keySet());
  429   			for (int i = disposableBeanNames.length - 1; i >= 0; i--) {
  430   				destroySingleton(disposableBeanNames[i]);
  431   			}
  432   		}
  433   
  434   		this.containedBeanMap.clear();
  435   		this.dependentBeanMap.clear();
  436   		this.dependenciesForBeanMap.clear();
  437   
  438   		synchronized (this.singletonObjects) {
  439   			this.singletonObjects.clear();
  440   			this.singletonFactories.clear();
  441   			this.earlySingletonObjects.clear();
  442   			this.registeredSingletons.clear();
  443   			this.singletonsCurrentlyInDestruction = false;
  444   		}
  445   	}
  446   
  447   	/**
  448   	 * Destroy the given bean. Delegates to <code>destroyBean</code>
  449   	 * if a corresponding disposable bean instance is found.
  450   	 * @param beanName the name of the bean
  451   	 * @see #destroyBean
  452   	 */
  453   	public void destroySingleton(String beanName) {
  454   		// Remove a registered singleton of the given name, if any.
  455   		removeSingleton(beanName);
  456   
  457   		// Destroy the corresponding DisposableBean instance.
  458   		DisposableBean disposableBean = null;
  459   		synchronized (this.disposableBeans) {
  460   			disposableBean = (DisposableBean) this.disposableBeans.remove(beanName);
  461   		}
  462   		destroyBean(beanName, disposableBean);
  463   	}
  464   
  465   	/**
  466   	 * Destroy the given bean. Must destroy beans that depend on the given
  467   	 * bean before the bean itself. Should not throw any exceptions.
  468   	 * @param beanName the name of the bean
  469   	 * @param bean the bean instance to destroy
  470   	 */
  471   	protected void destroyBean(String beanName, DisposableBean bean) {
  472   		// Trigger destruction of dependent beans first...
  473   		Set dependencies = (Set) this.dependentBeanMap.remove(beanName);
  474   		if (dependencies != null) {
  475   			if (logger.isDebugEnabled()) {
  476   				logger.debug("Retrieved dependent beans for bean '" + beanName + "': " + dependencies);
  477   			}
  478   			for (Iterator it = dependencies.iterator(); it.hasNext();) {
  479   				String dependentBeanName = (String) it.next();
  480   				destroySingleton(dependentBeanName);
  481   			}
  482   		}
  483   
  484   		// Actually destroy the bean now...
  485   		if (bean != null) {
  486   			try {
  487   				bean.destroy();
  488   			}
  489   			catch (Throwable ex) {
  490   				logger.error("Destroy method on bean with name '" + beanName + "' threw an exception", ex);
  491   			}
  492   		}
  493   
  494   		// Trigger destruction of contained beans...
  495   		Set containedBeans = (Set) this.containedBeanMap.remove(beanName);
  496   		if (containedBeans != null) {
  497   			for (Iterator it = containedBeans.iterator(); it.hasNext();) {
  498   				String containedBeanName = (String) it.next();
  499   				destroySingleton(containedBeanName);
  500   			}
  501   		}
  502   
  503   		// Remove destroyed bean from other beans' dependencies.
  504   		synchronized (this.dependentBeanMap) {
  505   			for (Iterator it = this.dependentBeanMap.entrySet().iterator(); it.hasNext();) {
  506   				Map.Entry entry = (Map.Entry) it.next();
  507   				Set dependenciesToClean = (Set) entry.getValue();
  508   				dependenciesToClean.remove(beanName);
  509   				if (dependenciesToClean.isEmpty()) {
  510   					it.remove();
  511   				}
  512   			}
  513   		}
  514   
  515   		// Remove destroyed bean's prepared dependency information.
  516   		this.dependenciesForBeanMap.remove(beanName);
  517   	}
  518   
  519   	/**
  520   	 * Expose the singleton mutex to subclasses.
  521   	 * <p>Subclasses should synchronize on the given Object if they perform
  522   	 * any sort of extended singleton creation phase. In particular, subclasses
  523   	 * should <i>not</i> have their own mutexes involved in singleton creation,
  524   	 * to avoid the potential for deadlocks in lazy-init situations.
  525   	 */
  526   	protected final Object getSingletonMutex() {
  527   		return this.singletonObjects;
  528   	}
  529   
  530   }

Save This Page
Home » spring-framework-2.5.6-with-dependencies » org.springframework » beans » factory » support » [javadoc | source]