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