001: /*
002: * Copyright 2002-2007 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: * http://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:
017: package org.springframework.beans.factory.support;
018:
019: import java.util.ArrayList;
020: import java.util.HashMap;
021: import java.util.Iterator;
022: import java.util.List;
023: import java.util.Map;
024:
025: import org.springframework.beans.BeansException;
026: import org.springframework.beans.factory.BeanCreationException;
027: import org.springframework.beans.factory.BeanCurrentlyInCreationException;
028: import org.springframework.beans.factory.BeanDefinitionStoreException;
029: import org.springframework.beans.factory.BeanFactory;
030: import org.springframework.beans.factory.BeanFactoryUtils;
031: import org.springframework.beans.factory.CannotLoadBeanClassException;
032: import org.springframework.beans.factory.FactoryBean;
033: import org.springframework.beans.factory.NoSuchBeanDefinitionException;
034: import org.springframework.beans.factory.config.BeanDefinition;
035: import org.springframework.beans.factory.config.ConfigurableBeanFactory;
036: import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
037: import org.springframework.core.CollectionFactory;
038: import org.springframework.util.Assert;
039: import org.springframework.util.ObjectUtils;
040: import org.springframework.util.StringUtils;
041:
042: /**
043: * Default implementation of the
044: * {@link org.springframework.beans.factory.ListableBeanFactory} and
045: * {@link BeanDefinitionRegistry} interfaces: a full-fledged bean factory
046: * based on bean definition objects.
047: *
048: * <p>Typical usage is registering all bean definitions first (possibly read
049: * from a bean definition file), before accessing beans. Bean definition lookup
050: * is therefore an inexpensive operation in a local bean definition table,
051: * operating on pre-built bean definition metadata objects.
052: *
053: * <p>Can be used as a standalone bean factory, or as a superclass for custom
054: * bean factories. Note that readers for specific bean definition formats are
055: * typically implemented separately rather than as bean factory subclasses:
056: * see for example {@link PropertiesBeanDefinitionReader} and
057: * {@link org.springframework.beans.factory.xml.XmlBeanDefinitionReader}.
058: *
059: * <p>For an alternative implementation of the
060: * {@link org.springframework.beans.factory.ListableBeanFactory} interface,
061: * have a look at {@link StaticListableBeanFactory}, which manages existing
062: * bean instances rather than creating new ones based on bean definitions.
063: *
064: * @author Rod Johnson
065: * @author Juergen Hoeller
066: * @since 16 April 2001
067: * @see StaticListableBeanFactory
068: * @see PropertiesBeanDefinitionReader
069: * @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader
070: */
071: public class DefaultListableBeanFactory extends
072: AbstractAutowireCapableBeanFactory implements
073: ConfigurableListableBeanFactory, BeanDefinitionRegistry {
074:
075: /** Whether to allow re-registration of a different definition with the same name */
076: private boolean allowBeanDefinitionOverriding = true;
077:
078: /** Whether to allow eager class loading even for lazy-init beans */
079: private boolean allowEagerClassLoading = true;
080:
081: /** Map of bean definition objects, keyed by bean name */
082: private final Map beanDefinitionMap = new HashMap();
083:
084: /** List of bean definition names, in registration order */
085: private final List beanDefinitionNames = new ArrayList();
086:
087: /**
088: * Create a new DefaultListableBeanFactory.
089: */
090: public DefaultListableBeanFactory() {
091: super ();
092: }
093:
094: /**
095: * Create a new DefaultListableBeanFactory with the given parent.
096: * @param parentBeanFactory the parent BeanFactory
097: */
098: public DefaultListableBeanFactory(BeanFactory parentBeanFactory) {
099: super (parentBeanFactory);
100: }
101:
102: /**
103: * Set whether it should be allowed to override bean definitions by registering
104: * a different definition with the same name, automatically replacing the former.
105: * If not, an exception will be thrown. Default is "true".
106: */
107: public void setAllowBeanDefinitionOverriding(
108: boolean allowBeanDefinitionOverriding) {
109: this .allowBeanDefinitionOverriding = allowBeanDefinitionOverriding;
110: }
111:
112: /**
113: * Set whether the factory is allowed to eagerly load bean classes
114: * even for bean definitions that are marked as "lazy-init".
115: * <p>Default is "true". Turn this flag off to suppress class loading
116: * for lazy-init beans unless such a bean is explicitly requested.
117: * In particular, by-type lookups will then simply ignore bean definitions
118: * without resolved class name, instead of loading the bean classes on
119: * demand just to perform a type check.
120: * @see AbstractBeanDefinition#setLazyInit
121: */
122: public void setAllowEagerClassLoading(boolean allowEagerClassLoading) {
123: this .allowEagerClassLoading = allowEagerClassLoading;
124: }
125:
126: public void copyConfigurationFrom(
127: ConfigurableBeanFactory otherFactory) {
128: super .copyConfigurationFrom(otherFactory);
129: if (otherFactory instanceof DefaultListableBeanFactory) {
130: DefaultListableBeanFactory otherListableFactory = (DefaultListableBeanFactory) otherFactory;
131: this .allowBeanDefinitionOverriding = otherListableFactory.allowBeanDefinitionOverriding;
132: this .allowEagerClassLoading = otherListableFactory.allowEagerClassLoading;
133: }
134: }
135:
136: //---------------------------------------------------------------------
137: // Implementation of ListableBeanFactory interface
138: //---------------------------------------------------------------------
139:
140: public boolean containsBeanDefinition(String beanName) {
141: return this .beanDefinitionMap.containsKey(beanName);
142: }
143:
144: public int getBeanDefinitionCount() {
145: return this .beanDefinitionMap.size();
146: }
147:
148: public String[] getBeanDefinitionNames() {
149: return StringUtils.toStringArray(this .beanDefinitionNames);
150: }
151:
152: public String[] getBeanNamesForType(Class type) {
153: return getBeanNamesForType(type, true, true);
154: }
155:
156: public String[] getBeanNamesForType(Class type,
157: boolean includePrototypes, boolean allowEagerInit) {
158: List result = new ArrayList();
159:
160: // Check all bean definitions.
161: for (Iterator it = this .beanDefinitionNames.iterator(); it
162: .hasNext();) {
163: String beanName = (String) it.next();
164: // Only consider bean as eligible if the bean name
165: // is not defined as alias for some other bean.
166: if (!isAlias(beanName)) {
167: RootBeanDefinition mbd = getMergedBeanDefinition(
168: beanName, false);
169: // Only check bean definition if it is complete.
170: if (!mbd.isAbstract()
171: && (allowEagerInit || mbd.hasBeanClass()
172: || !mbd.isLazyInit() || this .allowEagerClassLoading)) {
173: // In case of FactoryBean, match object created by FactoryBean.
174: try {
175: boolean isFactoryBean = isBeanClassMatch(
176: beanName, mbd, FactoryBean.class);
177: boolean matchFound = (allowEagerInit || !requiresEagerInitForType(
178: beanName, isFactoryBean, mbd
179: .getFactoryBeanName()))
180: && (includePrototypes || isSingleton(beanName))
181: && isTypeMatch(beanName, type);
182: if (!matchFound && isFactoryBean) {
183: // In case of FactoryBean, try to match FactoryBean instance itself next.
184: beanName = FACTORY_BEAN_PREFIX + beanName;
185: matchFound = (includePrototypes || mbd
186: .isSingleton())
187: && isTypeMatch(beanName, type);
188: }
189: if (matchFound) {
190: result.add(beanName);
191: }
192: } catch (CannotLoadBeanClassException ex) {
193: // Probably contains a placeholder: let's ignore it for type matching purposes.
194: if (logger.isDebugEnabled()) {
195: logger.debug(
196: "Ignoring bean class loading failure for bean '"
197: + beanName + "'", ex);
198: }
199: }
200: }
201: }
202: }
203:
204: // Check singletons too, to catch manually registered singletons.
205: String[] singletonNames = getSingletonNames();
206: for (int i = 0; i < singletonNames.length; i++) {
207: String beanName = singletonNames[i];
208: // Only check if manually registered.
209: if (!containsBeanDefinition(beanName)) {
210: // In case of FactoryBean, match object created by FactoryBean.
211: if (isFactoryBean(beanName)) {
212: if ((includePrototypes || isSingleton(beanName))
213: && isTypeMatch(beanName, type)) {
214: result.add(beanName);
215: // Match found for this bean: do not match FactoryBean itself anymore.
216: continue;
217: }
218: // In case of FactoryBean, try to match FactoryBean itself next.
219: beanName = FACTORY_BEAN_PREFIX + beanName;
220: }
221: // Match raw bean instance (might be raw FactoryBean).
222: if (isTypeMatch(beanName, type)) {
223: result.add(beanName);
224: }
225: }
226: }
227:
228: return StringUtils.toStringArray(result);
229: }
230:
231: /**
232: * Check whether the specified bean would need to be eagerly initialized
233: * in order to determine its type.
234: * @param beanName the name of the bean
235: * @param isFactoryBean whether the bean itself is a FactoryBean
236: * @param factoryBeanName a factory-bean reference that the bean definition
237: * defines a factory method for
238: * @return whether eager initialization is necessary
239: */
240: private boolean requiresEagerInitForType(String beanName,
241: boolean isFactoryBean, String factoryBeanName) {
242: return (isFactoryBean && !containsSingleton(beanName))
243: || (factoryBeanName != null
244: && isFactoryBean(factoryBeanName) && !containsSingleton(factoryBeanName));
245: }
246:
247: public Map getBeansOfType(Class type) throws BeansException {
248: return getBeansOfType(type, true, true);
249: }
250:
251: public Map getBeansOfType(Class type, boolean includePrototypes,
252: boolean allowEagerInit) throws BeansException {
253:
254: String[] beanNames = getBeanNamesForType(type,
255: includePrototypes, allowEagerInit);
256: Map result = CollectionFactory
257: .createLinkedMapIfPossible(beanNames.length);
258: for (int i = 0; i < beanNames.length; i++) {
259: String beanName = beanNames[i];
260: try {
261: result.put(beanName, getBean(beanName));
262: } catch (BeanCreationException ex) {
263: Throwable rootCause = ex.getMostSpecificCause();
264: if (rootCause instanceof BeanCurrentlyInCreationException) {
265: BeanCreationException bce = (BeanCreationException) rootCause;
266: if (isCurrentlyInCreation(bce.getBeanName())) {
267: if (logger.isDebugEnabled()) {
268: logger
269: .debug("Ignoring match to currently created bean '"
270: + beanName
271: + "': "
272: + ex.getMessage());
273: }
274: // Ignore: indicates a circular reference when autowiring constructors.
275: // We want to find matches other than the currently created bean itself.
276: continue;
277: }
278: }
279: throw ex;
280: }
281: }
282: return result;
283: }
284:
285: //---------------------------------------------------------------------
286: // Implementation of ConfigurableListableBeanFactory interface
287: //---------------------------------------------------------------------
288:
289: public void preInstantiateSingletons() throws BeansException {
290: if (logger.isInfoEnabled()) {
291: logger.info("Pre-instantiating singletons in " + this );
292: }
293: for (Iterator it = this .beanDefinitionNames.iterator(); it
294: .hasNext();) {
295: String beanName = (String) it.next();
296: if (!containsSingleton(beanName)
297: && containsBeanDefinition(beanName)) {
298: RootBeanDefinition bd = getMergedBeanDefinition(
299: beanName, false);
300: if (!bd.isAbstract() && bd.isSingleton()
301: && !bd.isLazyInit()) {
302: Class beanClass = resolveBeanClass(bd, beanName);
303: if (beanClass != null
304: && FactoryBean.class
305: .isAssignableFrom(beanClass)) {
306: getBean(FACTORY_BEAN_PREFIX + beanName);
307: } else {
308: getBean(beanName);
309: }
310: }
311: }
312: }
313: }
314:
315: //---------------------------------------------------------------------
316: // Implementation of BeanDefinitionRegistry interface
317: //---------------------------------------------------------------------
318:
319: public void registerBeanDefinition(String beanName,
320: BeanDefinition beanDefinition)
321: throws BeanDefinitionStoreException {
322:
323: Assert.hasText(beanName, "'beanName' must not be empty");
324: Assert.notNull(beanDefinition,
325: "BeanDefinition must not be null");
326:
327: if (beanDefinition instanceof AbstractBeanDefinition) {
328: try {
329: ((AbstractBeanDefinition) beanDefinition).validate();
330: } catch (BeanDefinitionValidationException ex) {
331: throw new BeanDefinitionStoreException(beanDefinition
332: .getResourceDescription(), beanName,
333: "Validation of bean definition failed", ex);
334: }
335: }
336:
337: Object oldBeanDefinition = this .beanDefinitionMap.get(beanName);
338: if (oldBeanDefinition != null) {
339: if (!this .allowBeanDefinitionOverriding) {
340: throw new BeanDefinitionStoreException(beanDefinition
341: .getResourceDescription(), beanName,
342: "Cannot register bean definition ["
343: + beanDefinition + "] for bean '"
344: + beanName + "': There is already ["
345: + oldBeanDefinition + "] bound.");
346: } else {
347: if (logger.isInfoEnabled()) {
348: logger.info("Overriding bean definition for bean '"
349: + beanName + "': replacing ["
350: + oldBeanDefinition + "] with ["
351: + beanDefinition + "]");
352: }
353: }
354: } else {
355: this .beanDefinitionNames.add(beanName);
356: }
357: this .beanDefinitionMap.put(beanName, beanDefinition);
358:
359: // Remove the merged bean definition for the given bean, if already created.
360: clearMergedBeanDefinition(beanName);
361:
362: // Remove corresponding bean from singleton cache, if any. Shouldn't usually
363: // be necessary, rather just meant for overriding a context's default beans
364: // (e.g. the default StaticMessageSource in a StaticApplicationContext).
365: synchronized (getSingletonMutex()) {
366: removeSingleton(beanName);
367: }
368: }
369:
370: //---------------------------------------------------------------------
371: // Implementation of superclass abstract methods
372: //---------------------------------------------------------------------
373:
374: public BeanDefinition getBeanDefinition(String beanName)
375: throws NoSuchBeanDefinitionException {
376: BeanDefinition bd = (BeanDefinition) this .beanDefinitionMap
377: .get(beanName);
378: if (bd == null) {
379: if (logger.isTraceEnabled()) {
380: logger.trace("No bean named '" + beanName
381: + "' found in " + this );
382: }
383: throw new NoSuchBeanDefinitionException(beanName);
384: }
385: return bd;
386: }
387:
388: protected Map findAutowireCandidates(String beanName,
389: Class requiredType) {
390: String[] candidateNames = BeanFactoryUtils
391: .beanNamesForTypeIncludingAncestors(this , requiredType);
392: Map result = CollectionFactory
393: .createLinkedMapIfPossible(candidateNames.length);
394: for (int i = 0; i < candidateNames.length; i++) {
395: String candidateName = candidateNames[i];
396: if (!candidateName.equals(beanName)
397: && (!containsBeanDefinition(candidateName) || getMergedBeanDefinition(
398: candidateName).isAutowireCandidate())) {
399: result.put(candidateName, getBean(candidateName));
400: }
401: }
402: return result;
403: }
404:
405: public String toString() {
406: StringBuffer sb = new StringBuffer(ObjectUtils
407: .identityToString(this ));
408: sb.append(": defining beans [");
409: sb.append(StringUtils
410: .arrayToCommaDelimitedString(getBeanDefinitionNames()));
411: sb.append("]; ");
412: BeanFactory parent = getParentBeanFactory();
413: if (parent == null) {
414: sb.append("root of factory hierarchy");
415: } else {
416: sb
417: .append("parent: "
418: + ObjectUtils.identityToString(parent));
419: }
420: return sb.toString();
421: }
422:
423: }
|