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;
018:
019: import java.util.ArrayList;
020: import java.util.Arrays;
021: import java.util.Iterator;
022: import java.util.LinkedHashMap;
023: import java.util.List;
024: import java.util.Map;
025:
026: import org.springframework.beans.BeansException;
027: import org.springframework.util.Assert;
028: import org.springframework.util.StringUtils;
029:
030: /**
031: * Convenience methods operating on bean factories, in particular
032: * on the {@link ListableBeanFactory} interface.
033: *
034: * <p>Returns bean counts, bean names or bean instances,
035: * taking into account the nesting hierarchy of a bean factory
036: * (which the methods defined on the ListableBeanFactory interface don't,
037: * in contrast to the methods defined on the BeanFactory interface).
038: *
039: * @author Rod Johnson
040: * @author Juergen Hoeller
041: * @since 04.07.2003
042: */
043: public abstract class BeanFactoryUtils {
044:
045: /**
046: * Separator for generated bean names. If a class name or parent name is not
047: * unique, "#1", "#2" etc will be appended, until the name becomes unique.
048: */
049: public static final String GENERATED_BEAN_NAME_SEPARATOR = "#";
050:
051: /**
052: * Return whether the given name is a factory dereference
053: * (beginning with the factory dereference prefix).
054: * @param name the name of the bean
055: * @return whether the given name is a factory dereference
056: * @see BeanFactory#FACTORY_BEAN_PREFIX
057: */
058: public static boolean isFactoryDereference(String name) {
059: return (name != null && name
060: .startsWith(BeanFactory.FACTORY_BEAN_PREFIX));
061: }
062:
063: /**
064: * Return the actual bean name, stripping out the factory dereference
065: * prefix (if any, also stripping repeated factory prefixes if found).
066: * @param name the name of the bean
067: * @return the transformed name
068: * @see BeanFactory#FACTORY_BEAN_PREFIX
069: */
070: public static String transformedBeanName(String name) {
071: Assert.notNull(name, "'name' must not be null");
072: String beanName = name;
073: while (beanName.startsWith(BeanFactory.FACTORY_BEAN_PREFIX)) {
074: beanName = beanName
075: .substring(BeanFactory.FACTORY_BEAN_PREFIX.length());
076: }
077: return beanName;
078: }
079:
080: /**
081: * Return whether the given name is a bean name which has been generated
082: * by the default naming strategy (containing a "#..." part).
083: * @param name the name of the bean
084: * @return whether the given name is a generated bean name
085: * @see #GENERATED_BEAN_NAME_SEPARATOR
086: * @see org.springframework.beans.factory.support.BeanDefinitionReaderUtils#generateBeanName
087: * @see org.springframework.beans.factory.support.DefaultBeanNameGenerator
088: */
089: public static boolean isGeneratedBeanName(String name) {
090: return (name != null && name
091: .indexOf(GENERATED_BEAN_NAME_SEPARATOR) != -1);
092: }
093:
094: /**
095: * Extract the "raw" bean name from the given (potentially generated) bean name,
096: * excluding any "#..." suffixes which might have been added for uniqueness.
097: * @param name the potentially generated bean name
098: * @return the raw bean name
099: * @see #GENERATED_BEAN_NAME_SEPARATOR
100: */
101: public static String originalBeanName(String name) {
102: Assert.notNull(name, "'name' must not be null");
103: int separatorIndex = name
104: .indexOf(GENERATED_BEAN_NAME_SEPARATOR);
105: return (separatorIndex != -1 ? name
106: .substring(0, separatorIndex) : name);
107: }
108:
109: /**
110: * Count all beans in any hierarchy in which this factory participates.
111: * Includes counts of ancestor bean factories.
112: * <p>Beans that are "overridden" (specified in a descendant factory
113: * with the same name) are only counted once.
114: * @param lbf the bean factory
115: * @return count of beans including those defined in ancestor factories
116: */
117: public static int countBeansIncludingAncestors(
118: ListableBeanFactory lbf) {
119: return beanNamesIncludingAncestors(lbf).length;
120: }
121:
122: /**
123: * Return all bean names in the factory, including ancestor factories.
124: * @param lbf the bean factory
125: * @return the array of matching bean names, or an empty array if none
126: * @see #beanNamesForTypeIncludingAncestors
127: */
128: public static String[] beanNamesIncludingAncestors(
129: ListableBeanFactory lbf) {
130: return beanNamesForTypeIncludingAncestors(lbf, Object.class);
131: }
132:
133: /**
134: * Get all bean names for the given type, including those defined in ancestor
135: * factories. Will return unique names in case of overridden bean definitions.
136: * <p>Does consider objects created by FactoryBeans, which means that FactoryBeans
137: * will get initialized. If the object created by the FactoryBean doesn't match,
138: * the raw FactoryBean itself will be matched against the type.
139: * <p>This version of <code>beanNamesForTypeIncludingAncestors</code> automatically
140: * includes prototypes and FactoryBeans.
141: * @param lbf the bean factory
142: * @param type the type that beans must match
143: * @return the array of matching bean names, or an empty array if none
144: */
145: public static String[] beanNamesForTypeIncludingAncestors(
146: ListableBeanFactory lbf, Class type) {
147: Assert.notNull(lbf, "ListableBeanFactory must not be null");
148: String[] result = lbf.getBeanNamesForType(type);
149: if (lbf instanceof HierarchicalBeanFactory) {
150: HierarchicalBeanFactory hbf = (HierarchicalBeanFactory) lbf;
151: if (hbf.getParentBeanFactory() instanceof ListableBeanFactory) {
152: String[] parentResult = beanNamesForTypeIncludingAncestors(
153: (ListableBeanFactory) hbf
154: .getParentBeanFactory(), type);
155: List resultList = new ArrayList();
156: resultList.addAll(Arrays.asList(result));
157: for (int i = 0; i < parentResult.length; i++) {
158: String beanName = parentResult[i];
159: if (!resultList.contains(beanName)
160: && !hbf.containsLocalBean(beanName)) {
161: resultList.add(beanName);
162: }
163: }
164: result = StringUtils.toStringArray(resultList);
165: }
166: }
167: return result;
168: }
169:
170: /**
171: * Get all bean names for the given type, including those defined in ancestor
172: * factories. Will return unique names in case of overridden bean definitions.
173: * <p>Does consider objects created by FactoryBeans if the "allowEagerInit"
174: * flag is set, which means that FactoryBeans will get initialized. If the
175: * object created by the FactoryBean doesn't match, the raw FactoryBean itself
176: * will be matched against the type. If "allowEagerInit" is not set,
177: * only raw FactoryBeans will be checked (which doesn't require initialization
178: * of each FactoryBean).
179: * @param lbf the bean factory
180: * @param includePrototypes whether to include prototype beans too or just singletons
181: * (also applies to FactoryBeans)
182: * @param allowEagerInit whether to initialize <i>lazy-init singletons</i> and
183: * <i>objects created by FactoryBeans</i> (or by factory methods with a
184: * "factory-bean" reference) for the type check. Note that FactoryBeans need to be
185: * eagerly initialized to determine their type: So be aware that passing in "true"
186: * for this flag will initialize FactoryBeans and "factory-bean" references.
187: * @param type the type that beans must match
188: * @return the array of matching bean names, or an empty array if none
189: */
190: public static String[] beanNamesForTypeIncludingAncestors(
191: ListableBeanFactory lbf, Class type,
192: boolean includePrototypes, boolean allowEagerInit) {
193:
194: Assert.notNull(lbf, "ListableBeanFactory must not be null");
195: String[] result = lbf.getBeanNamesForType(type,
196: includePrototypes, allowEagerInit);
197: if (lbf instanceof HierarchicalBeanFactory) {
198: HierarchicalBeanFactory hbf = (HierarchicalBeanFactory) lbf;
199: if (hbf.getParentBeanFactory() instanceof ListableBeanFactory) {
200: String[] parentResult = beanNamesForTypeIncludingAncestors(
201: (ListableBeanFactory) hbf
202: .getParentBeanFactory(), type,
203: includePrototypes, allowEagerInit);
204: List resultList = new ArrayList();
205: resultList.addAll(Arrays.asList(result));
206: for (int i = 0; i < parentResult.length; i++) {
207: String beanName = parentResult[i];
208: if (!resultList.contains(beanName)
209: && !hbf.containsLocalBean(beanName)) {
210: resultList.add(beanName);
211: }
212: }
213: result = StringUtils.toStringArray(resultList);
214: }
215: }
216: return result;
217: }
218:
219: /**
220: * Return all beans of the given type or subtypes, also picking up beans defined in
221: * ancestor bean factories if the current bean factory is a HierarchicalBeanFactory.
222: * The returned Map will only contain beans of this type.
223: * <p>Does consider objects created by FactoryBeans, which means that FactoryBeans
224: * will get initialized. If the object created by the FactoryBean doesn't match,
225: * the raw FactoryBean itself will be matched against the type.
226: * @param lbf the bean factory
227: * @param type type of bean to match
228: * @return the Map of matching bean instances, or an empty Map if none
229: * @throws BeansException if a bean could not be created
230: */
231: public static Map beansOfTypeIncludingAncestors(
232: ListableBeanFactory lbf, Class type) throws BeansException {
233:
234: Assert.notNull(lbf, "ListableBeanFactory must not be null");
235: Map result = new LinkedHashMap(4);
236: result.putAll(lbf.getBeansOfType(type));
237: if (lbf instanceof HierarchicalBeanFactory) {
238: HierarchicalBeanFactory hbf = (HierarchicalBeanFactory) lbf;
239: if (hbf.getParentBeanFactory() instanceof ListableBeanFactory) {
240: Map parentResult = beansOfTypeIncludingAncestors(
241: (ListableBeanFactory) hbf
242: .getParentBeanFactory(), type);
243: for (Iterator it = parentResult.entrySet().iterator(); it
244: .hasNext();) {
245: Map.Entry entry = (Map.Entry) it.next();
246: String beanName = (String) entry.getKey();
247: if (!result.containsKey(beanName)
248: && !hbf.containsLocalBean(beanName)) {
249: result.put(beanName, entry.getValue());
250: }
251: }
252: }
253: }
254: return result;
255: }
256:
257: /**
258: * Return all beans of the given type or subtypes, also picking up beans defined in
259: * ancestor bean factories if the current bean factory is a HierarchicalBeanFactory.
260: * The returned Map will only contain beans of this type.
261: * <p>Does consider objects created by FactoryBeans if the "allowEagerInit"
262: * flag is set, which means that FactoryBeans will get initialized. If the
263: * object created by the FactoryBean doesn't match, the raw FactoryBean itself
264: * will be matched against the type. If "allowEagerInit" is not set,
265: * only raw FactoryBeans will be checked (which doesn't require initialization
266: * of each FactoryBean).
267: * @param lbf the bean factory
268: * @param type type of bean to match
269: * @param includePrototypes whether to include prototype beans too or just singletons
270: * (also applies to FactoryBeans)
271: * @param allowEagerInit whether to initialize <i>lazy-init singletons</i> and
272: * <i>objects created by FactoryBeans</i> (or by factory methods with a
273: * "factory-bean" reference) for the type check. Note that FactoryBeans need to be
274: * eagerly initialized to determine their type: So be aware that passing in "true"
275: * for this flag will initialize FactoryBeans and "factory-bean" references.
276: * @return the Map of matching bean instances, or an empty Map if none
277: * @throws BeansException if a bean could not be created
278: */
279: public static Map beansOfTypeIncludingAncestors(
280: ListableBeanFactory lbf, Class type,
281: boolean includePrototypes, boolean allowEagerInit)
282: throws BeansException {
283:
284: Assert.notNull(lbf, "ListableBeanFactory must not be null");
285: Map result = new LinkedHashMap(4);
286: result.putAll(lbf.getBeansOfType(type, includePrototypes,
287: allowEagerInit));
288: if (lbf instanceof HierarchicalBeanFactory) {
289: HierarchicalBeanFactory hbf = (HierarchicalBeanFactory) lbf;
290: if (hbf.getParentBeanFactory() instanceof ListableBeanFactory) {
291: Map parentResult = beansOfTypeIncludingAncestors(
292: (ListableBeanFactory) hbf
293: .getParentBeanFactory(), type,
294: includePrototypes, allowEagerInit);
295: for (Iterator it = parentResult.entrySet().iterator(); it
296: .hasNext();) {
297: Map.Entry entry = (Map.Entry) it.next();
298: String beanName = (String) entry.getKey();
299: if (!result.containsKey(beanName)
300: && !hbf.containsLocalBean(beanName)) {
301: result.put(beanName, entry.getValue());
302: }
303: }
304: }
305: }
306: return result;
307: }
308:
309: /**
310: * Return a single bean of the given type or subtypes, also picking up beans
311: * defined in ancestor bean factories if the current bean factory is a
312: * HierarchicalBeanFactory. Useful convenience method when we expect a
313: * single bean and don't care about the bean name.
314: * <p>Does consider objects created by FactoryBeans, which means that FactoryBeans
315: * will get initialized. If the object created by the FactoryBean doesn't match,
316: * the raw FactoryBean itself will be matched against the type.
317: * <p>This version of <code>beanOfTypeIncludingAncestors</code> automatically includes
318: * prototypes and FactoryBeans.
319: * @param lbf the bean factory
320: * @param type type of bean to match
321: * @return the matching bean instance
322: * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException
323: * if 0 or more than 1 beans of the given type were found
324: * @throws BeansException if the bean could not be created
325: */
326: public static Object beanOfTypeIncludingAncestors(
327: ListableBeanFactory lbf, Class type) throws BeansException {
328:
329: Map beansOfType = beansOfTypeIncludingAncestors(lbf, type);
330: if (beansOfType.size() == 1) {
331: return beansOfType.values().iterator().next();
332: } else {
333: throw new NoSuchBeanDefinitionException(type,
334: "expected single bean but found "
335: + beansOfType.size());
336: }
337: }
338:
339: /**
340: * Return a single bean of the given type or subtypes, also picking up beans
341: * defined in ancestor bean factories if the current bean factory is a
342: * HierarchicalBeanFactory. Useful convenience method when we expect a
343: * single bean and don't care about the bean name.
344: * <p>Does consider objects created by FactoryBeans if the "allowEagerInit"
345: * flag is set, which means that FactoryBeans will get initialized. If the
346: * object created by the FactoryBean doesn't match, the raw FactoryBean itself
347: * will be matched against the type. If "allowEagerInit" is not set,
348: * only raw FactoryBeans will be checked (which doesn't require initialization
349: * of each FactoryBean).
350: * @param lbf the bean factory
351: * @param type type of bean to match
352: * @param includePrototypes whether to include prototype beans too or just singletons
353: * (also applies to FactoryBeans)
354: * @param allowEagerInit whether to initialize <i>lazy-init singletons</i> and
355: * <i>objects created by FactoryBeans</i> (or by factory methods with a
356: * "factory-bean" reference) for the type check. Note that FactoryBeans need to be
357: * eagerly initialized to determine their type: So be aware that passing in "true"
358: * for this flag will initialize FactoryBeans and "factory-bean" references.
359: * @return the matching bean instance
360: * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException
361: * if 0 or more than 1 beans of the given type were found
362: * @throws BeansException if the bean could not be created
363: */
364: public static Object beanOfTypeIncludingAncestors(
365: ListableBeanFactory lbf, Class type,
366: boolean includePrototypes, boolean allowEagerInit)
367: throws BeansException {
368:
369: Map beansOfType = beansOfTypeIncludingAncestors(lbf, type,
370: includePrototypes, allowEagerInit);
371: if (beansOfType.size() == 1) {
372: return beansOfType.values().iterator().next();
373: } else {
374: throw new NoSuchBeanDefinitionException(type,
375: "expected single bean but found "
376: + beansOfType.size());
377: }
378: }
379:
380: /**
381: * Return a single bean of the given type or subtypes, not looking in ancestor
382: * factories. Useful convenience method when we expect a single bean and
383: * don't care about the bean name.
384: * <p>Does consider objects created by FactoryBeans, which means that FactoryBeans
385: * will get initialized. If the object created by the FactoryBean doesn't match,
386: * the raw FactoryBean itself will be matched against the type.
387: * <p>This version of <code>beanOfType</code> automatically includes
388: * prototypes and FactoryBeans.
389: * @param lbf the bean factory
390: * @param type type of bean to match
391: * @return the matching bean instance
392: * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException
393: * if 0 or more than 1 beans of the given type were found
394: * @throws BeansException if the bean could not be created
395: */
396: public static Object beanOfType(ListableBeanFactory lbf, Class type)
397: throws BeansException {
398: Assert.notNull(lbf, "ListableBeanFactory must not be null");
399: Map beansOfType = lbf.getBeansOfType(type);
400: if (beansOfType.size() == 1) {
401: return beansOfType.values().iterator().next();
402: } else {
403: throw new NoSuchBeanDefinitionException(type,
404: "expected single bean but found "
405: + beansOfType.size());
406: }
407: }
408:
409: /**
410: * Return a single bean of the given type or subtypes, not looking in ancestor
411: * factories. Useful convenience method when we expect a single bean and
412: * don't care about the bean name.
413: * <p>Does consider objects created by FactoryBeans if the "allowEagerInit"
414: * flag is set, which means that FactoryBeans will get initialized. If the
415: * object created by the FactoryBean doesn't match, the raw FactoryBean itself
416: * will be matched against the type. If "allowEagerInit" is not set,
417: * only raw FactoryBeans will be checked (which doesn't require initialization
418: * of each FactoryBean).
419: * @param lbf the bean factory
420: * @param type type of bean to match
421: * @param includePrototypes whether to include prototype beans too or just singletons
422: * (also applies to FactoryBeans)
423: * @param allowEagerInit whether to initialize <i>lazy-init singletons</i> and
424: * <i>objects created by FactoryBeans</i> (or by factory methods with a
425: * "factory-bean" reference) for the type check. Note that FactoryBeans need to be
426: * eagerly initialized to determine their type: So be aware that passing in "true"
427: * for this flag will initialize FactoryBeans and "factory-bean" references.
428: * @return the matching bean instance
429: * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException
430: * if 0 or more than 1 beans of the given type were found
431: * @throws BeansException if the bean could not be created
432: */
433: public static Object beanOfType(ListableBeanFactory lbf,
434: Class type, boolean includePrototypes,
435: boolean allowEagerInit) throws BeansException {
436:
437: Assert.notNull(lbf, "ListableBeanFactory must not be null");
438: Map beansOfType = lbf.getBeansOfType(type, includePrototypes,
439: allowEagerInit);
440: if (beansOfType.size() == 1) {
441: return beansOfType.values().iterator().next();
442: } else {
443: throw new NoSuchBeanDefinitionException(type,
444: "expected single bean but found "
445: + beansOfType.size());
446: }
447: }
448:
449: }
|