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;
18
19 import java.util.ArrayList;
20 import java.util.Arrays;
21 import java.util.Iterator;
22 import java.util.LinkedHashMap;
23 import java.util.List;
24 import java.util.Map;
25
26 import org.springframework.beans.BeansException;
27 import org.springframework.util.Assert;
28 import org.springframework.util.StringUtils;
29
30 /**
31 * Convenience methods operating on bean factories, in particular
32 * on the {@link ListableBeanFactory} interface.
33 *
34 * <p>Returns bean counts, bean names or bean instances,
35 * taking into account the nesting hierarchy of a bean factory
36 * (which the methods defined on the ListableBeanFactory interface don't,
37 * in contrast to the methods defined on the BeanFactory interface).
38 *
39 * @author Rod Johnson
40 * @author Juergen Hoeller
41 * @since 04.07.2003
42 */
43 public abstract class BeanFactoryUtils {
44
45 /**
46 * Separator for generated bean names. If a class name or parent name is not
47 * unique, "#1", "#2" etc will be appended, until the name becomes unique.
48 */
49 public static final String GENERATED_BEAN_NAME_SEPARATOR = "#";
50
51
52 /**
53 * Return whether the given name is a factory dereference
54 * (beginning with the factory dereference prefix).
55 * @param name the name of the bean
56 * @return whether the given name is a factory dereference
57 * @see BeanFactory#FACTORY_BEAN_PREFIX
58 */
59 public static boolean isFactoryDereference(String name) {
60 return (name != null && name.startsWith(BeanFactory.FACTORY_BEAN_PREFIX));
61 }
62
63 /**
64 * Return the actual bean name, stripping out the factory dereference
65 * prefix (if any, also stripping repeated factory prefixes if found).
66 * @param name the name of the bean
67 * @return the transformed name
68 * @see BeanFactory#FACTORY_BEAN_PREFIX
69 */
70 public static String transformedBeanName(String name) {
71 Assert.notNull(name, "'name' must not be null");
72 String beanName = name;
73 while (beanName.startsWith(BeanFactory.FACTORY_BEAN_PREFIX)) {
74 beanName = beanName.substring(BeanFactory.FACTORY_BEAN_PREFIX.length());
75 }
76 return beanName;
77 }
78
79 /**
80 * Return whether the given name is a bean name which has been generated
81 * by the default naming strategy (containing a "#..." part).
82 * @param name the name of the bean
83 * @return whether the given name is a generated bean name
84 * @see #GENERATED_BEAN_NAME_SEPARATOR
85 * @see org.springframework.beans.factory.support.BeanDefinitionReaderUtils#generateBeanName
86 * @see org.springframework.beans.factory.support.DefaultBeanNameGenerator
87 */
88 public static boolean isGeneratedBeanName(String name) {
89 return (name != null && name.indexOf(GENERATED_BEAN_NAME_SEPARATOR) != -1);
90 }
91
92 /**
93 * Extract the "raw" bean name from the given (potentially generated) bean name,
94 * excluding any "#..." suffixes which might have been added for uniqueness.
95 * @param name the potentially generated bean name
96 * @return the raw bean name
97 * @see #GENERATED_BEAN_NAME_SEPARATOR
98 */
99 public static String originalBeanName(String name) {
100 Assert.notNull(name, "'name' must not be null");
101 int separatorIndex = name.indexOf(GENERATED_BEAN_NAME_SEPARATOR);
102 return (separatorIndex != -1 ? name.substring(0, separatorIndex) : name);
103 }
104
105
106 /**
107 * Count all beans in any hierarchy in which this factory participates.
108 * Includes counts of ancestor bean factories.
109 * <p>Beans that are "overridden" (specified in a descendant factory
110 * with the same name) are only counted once.
111 * @param lbf the bean factory
112 * @return count of beans including those defined in ancestor factories
113 */
114 public static int countBeansIncludingAncestors(ListableBeanFactory lbf) {
115 return beanNamesIncludingAncestors(lbf).length;
116 }
117
118 /**
119 * Return all bean names in the factory, including ancestor factories.
120 * @param lbf the bean factory
121 * @return the array of matching bean names, or an empty array if none
122 * @see #beanNamesForTypeIncludingAncestors
123 */
124 public static String[] beanNamesIncludingAncestors(ListableBeanFactory lbf) {
125 return beanNamesForTypeIncludingAncestors(lbf, Object.class);
126 }
127
128
129 /**
130 * Get all bean names for the given type, including those defined in ancestor
131 * factories. Will return unique names in case of overridden bean definitions.
132 * <p>Does consider objects created by FactoryBeans, which means that FactoryBeans
133 * will get initialized. If the object created by the FactoryBean doesn't match,
134 * the raw FactoryBean itself will be matched against the type.
135 * <p>This version of <code>beanNamesForTypeIncludingAncestors</code> automatically
136 * includes prototypes and FactoryBeans.
137 * @param lbf the bean factory
138 * @param type the type that beans must match
139 * @return the array of matching bean names, or an empty array if none
140 */
141 public static String[] beanNamesForTypeIncludingAncestors(ListableBeanFactory lbf, Class type) {
142 Assert.notNull(lbf, "ListableBeanFactory must not be null");
143 String[] result = lbf.getBeanNamesForType(type);
144 if (lbf instanceof HierarchicalBeanFactory) {
145 HierarchicalBeanFactory hbf = (HierarchicalBeanFactory) lbf;
146 if (hbf.getParentBeanFactory() instanceof ListableBeanFactory) {
147 String[] parentResult = beanNamesForTypeIncludingAncestors(
148 (ListableBeanFactory) hbf.getParentBeanFactory(), type);
149 List resultList = new ArrayList();
150 resultList.addAll(Arrays.asList(result));
151 for (int i = 0; i < parentResult.length; i++) {
152 String beanName = parentResult[i];
153 if (!resultList.contains(beanName) && !hbf.containsLocalBean(beanName)) {
154 resultList.add(beanName);
155 }
156 }
157 result = StringUtils.toStringArray(resultList);
158 }
159 }
160 return result;
161 }
162
163 /**
164 * Get all bean names for the given type, including those defined in ancestor
165 * factories. Will return unique names in case of overridden bean definitions.
166 * <p>Does consider objects created by FactoryBeans if the "allowEagerInit"
167 * flag is set, which means that FactoryBeans will get initialized. If the
168 * object created by the FactoryBean doesn't match, the raw FactoryBean itself
169 * will be matched against the type. If "allowEagerInit" is not set,
170 * only raw FactoryBeans will be checked (which doesn't require initialization
171 * of each FactoryBean).
172 * @param lbf the bean factory
173 * @param includeNonSingletons whether to include prototype or scoped beans too
174 * or just singletons (also applies to FactoryBeans)
175 * @param allowEagerInit whether to initialize <i>lazy-init singletons</i> and
176 * <i>objects created by FactoryBeans</i> (or by factory methods with a
177 * "factory-bean" reference) for the type check. Note that FactoryBeans need to be
178 * eagerly initialized to determine their type: So be aware that passing in "true"
179 * for this flag will initialize FactoryBeans and "factory-bean" references.
180 * @param type the type that beans must match
181 * @return the array of matching bean names, or an empty array if none
182 */
183 public static String[] beanNamesForTypeIncludingAncestors(
184 ListableBeanFactory lbf, Class type, boolean includeNonSingletons, boolean allowEagerInit) {
185
186 Assert.notNull(lbf, "ListableBeanFactory must not be null");
187 String[] result = lbf.getBeanNamesForType(type, includeNonSingletons, allowEagerInit);
188 if (lbf instanceof HierarchicalBeanFactory) {
189 HierarchicalBeanFactory hbf = (HierarchicalBeanFactory) lbf;
190 if (hbf.getParentBeanFactory() instanceof ListableBeanFactory) {
191 String[] parentResult = beanNamesForTypeIncludingAncestors(
192 (ListableBeanFactory) hbf.getParentBeanFactory(), type, includeNonSingletons, allowEagerInit);
193 List resultList = new ArrayList();
194 resultList.addAll(Arrays.asList(result));
195 for (int i = 0; i < parentResult.length; i++) {
196 String beanName = parentResult[i];
197 if (!resultList.contains(beanName) && !hbf.containsLocalBean(beanName)) {
198 resultList.add(beanName);
199 }
200 }
201 result = StringUtils.toStringArray(resultList);
202 }
203 }
204 return result;
205 }
206
207 /**
208 * Return all beans of the given type or subtypes, also picking up beans defined in
209 * ancestor bean factories if the current bean factory is a HierarchicalBeanFactory.
210 * The returned Map will only contain beans of this type.
211 * <p>Does consider objects created by FactoryBeans, which means that FactoryBeans
212 * will get initialized. If the object created by the FactoryBean doesn't match,
213 * the raw FactoryBean itself will be matched against the type.
214 * @param lbf the bean factory
215 * @param type type of bean to match
216 * @return the Map of matching bean instances, or an empty Map if none
217 * @throws BeansException if a bean could not be created
218 */
219 public static Map beansOfTypeIncludingAncestors(ListableBeanFactory lbf, Class type)
220 throws BeansException {
221
222 Assert.notNull(lbf, "ListableBeanFactory must not be null");
223 Map result = new LinkedHashMap(4);
224 result.putAll(lbf.getBeansOfType(type));
225 if (lbf instanceof HierarchicalBeanFactory) {
226 HierarchicalBeanFactory hbf = (HierarchicalBeanFactory) lbf;
227 if (hbf.getParentBeanFactory() instanceof ListableBeanFactory) {
228 Map parentResult = beansOfTypeIncludingAncestors(
229 (ListableBeanFactory) hbf.getParentBeanFactory(), type);
230 for (Iterator it = parentResult.entrySet().iterator(); it.hasNext();) {
231 Map.Entry entry = (Map.Entry) it.next();
232 String beanName = (String) entry.getKey();
233 if (!result.containsKey(beanName) && !hbf.containsLocalBean(beanName)) {
234 result.put(beanName, entry.getValue());
235 }
236 }
237 }
238 }
239 return result;
240 }
241
242 /**
243 * Return all beans of the given type or subtypes, also picking up beans defined in
244 * ancestor bean factories if the current bean factory is a HierarchicalBeanFactory.
245 * The returned Map will only contain beans of this type.
246 * <p>Does consider objects created by FactoryBeans if the "allowEagerInit"
247 * flag is set, which means that FactoryBeans will get initialized. If the
248 * object created by the FactoryBean doesn't match, the raw FactoryBean itself
249 * will be matched against the type. If "allowEagerInit" is not set,
250 * only raw FactoryBeans will be checked (which doesn't require initialization
251 * of each FactoryBean).
252 * @param lbf the bean factory
253 * @param type type of bean to match
254 * @param includeNonSingletons whether to include prototype or scoped beans too
255 * or just singletons (also applies to FactoryBeans)
256 * @param allowEagerInit whether to initialize <i>lazy-init singletons</i> and
257 * <i>objects created by FactoryBeans</i> (or by factory methods with a
258 * "factory-bean" reference) for the type check. Note that FactoryBeans need to be
259 * eagerly initialized to determine their type: So be aware that passing in "true"
260 * for this flag will initialize FactoryBeans and "factory-bean" references.
261 * @return the Map of matching bean instances, or an empty Map if none
262 * @throws BeansException if a bean could not be created
263 */
264 public static Map beansOfTypeIncludingAncestors(
265 ListableBeanFactory lbf, Class type, boolean includeNonSingletons, boolean allowEagerInit)
266 throws BeansException {
267
268 Assert.notNull(lbf, "ListableBeanFactory must not be null");
269 Map result = new LinkedHashMap(4);
270 result.putAll(lbf.getBeansOfType(type, includeNonSingletons, allowEagerInit));
271 if (lbf instanceof HierarchicalBeanFactory) {
272 HierarchicalBeanFactory hbf = (HierarchicalBeanFactory) lbf;
273 if (hbf.getParentBeanFactory() instanceof ListableBeanFactory) {
274 Map parentResult = beansOfTypeIncludingAncestors(
275 (ListableBeanFactory) hbf.getParentBeanFactory(), type, includeNonSingletons, allowEagerInit);
276 for (Iterator it = parentResult.entrySet().iterator(); it.hasNext();) {
277 Map.Entry entry = (Map.Entry) it.next();
278 String beanName = (String) entry.getKey();
279 if (!result.containsKey(beanName) && !hbf.containsLocalBean(beanName)) {
280 result.put(beanName, entry.getValue());
281 }
282 }
283 }
284 }
285 return result;
286 }
287
288
289 /**
290 * Return a single bean of the given type or subtypes, also picking up beans
291 * defined in ancestor bean factories if the current bean factory is a
292 * HierarchicalBeanFactory. Useful convenience method when we expect a
293 * single bean and don't care about the bean name.
294 * <p>Does consider objects created by FactoryBeans, which means that FactoryBeans
295 * will get initialized. If the object created by the FactoryBean doesn't match,
296 * the raw FactoryBean itself will be matched against the type.
297 * <p>This version of <code>beanOfTypeIncludingAncestors</code> automatically includes
298 * prototypes and FactoryBeans.
299 * @param lbf the bean factory
300 * @param type type of bean to match
301 * @return the matching bean instance
302 * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException
303 * if 0 or more than 1 beans of the given type were found
304 * @throws BeansException if the bean could not be created
305 */
306 public static Object beanOfTypeIncludingAncestors(ListableBeanFactory lbf, Class type)
307 throws BeansException {
308
309 Map beansOfType = beansOfTypeIncludingAncestors(lbf, type);
310 if (beansOfType.size() == 1) {
311 return beansOfType.values().iterator().next();
312 }
313 else {
314 throw new NoSuchBeanDefinitionException(type, "expected single bean but found " + beansOfType.size());
315 }
316 }
317
318 /**
319 * Return a single bean of the given type or subtypes, also picking up beans
320 * defined in ancestor bean factories if the current bean factory is a
321 * HierarchicalBeanFactory. Useful convenience method when we expect a
322 * single bean and don't care about the bean name.
323 * <p>Does consider objects created by FactoryBeans if the "allowEagerInit"
324 * flag is set, which means that FactoryBeans will get initialized. If the
325 * object created by the FactoryBean doesn't match, the raw FactoryBean itself
326 * will be matched against the type. If "allowEagerInit" is not set,
327 * only raw FactoryBeans will be checked (which doesn't require initialization
328 * of each FactoryBean).
329 * @param lbf the bean factory
330 * @param type type of bean to match
331 * @param includeNonSingletons whether to include prototype or scoped beans too
332 * or just singletons (also applies to FactoryBeans)
333 * @param allowEagerInit whether to initialize <i>lazy-init singletons</i> and
334 * <i>objects created by FactoryBeans</i> (or by factory methods with a
335 * "factory-bean" reference) for the type check. Note that FactoryBeans need to be
336 * eagerly initialized to determine their type: So be aware that passing in "true"
337 * for this flag will initialize FactoryBeans and "factory-bean" references.
338 * @return the matching bean instance
339 * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException
340 * if 0 or more than 1 beans of the given type were found
341 * @throws BeansException if the bean could not be created
342 */
343 public static Object beanOfTypeIncludingAncestors(
344 ListableBeanFactory lbf, Class type, boolean includeNonSingletons, boolean allowEagerInit)
345 throws BeansException {
346
347 Map beansOfType = beansOfTypeIncludingAncestors(lbf, type, includeNonSingletons, allowEagerInit);
348 if (beansOfType.size() == 1) {
349 return beansOfType.values().iterator().next();
350 }
351 else {
352 throw new NoSuchBeanDefinitionException(type, "expected single bean but found " + beansOfType.size());
353 }
354 }
355
356 /**
357 * Return a single bean of the given type or subtypes, not looking in ancestor
358 * factories. Useful convenience method when we expect a single bean and
359 * don't care about the bean name.
360 * <p>Does consider objects created by FactoryBeans, which means that FactoryBeans
361 * will get initialized. If the object created by the FactoryBean doesn't match,
362 * the raw FactoryBean itself will be matched against the type.
363 * <p>This version of <code>beanOfType</code> automatically includes
364 * prototypes and FactoryBeans.
365 * @param lbf the bean factory
366 * @param type type of bean to match
367 * @return the matching bean instance
368 * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException
369 * if 0 or more than 1 beans of the given type were found
370 * @throws BeansException if the bean could not be created
371 */
372 public static Object beanOfType(ListableBeanFactory lbf, Class type) throws BeansException {
373 Assert.notNull(lbf, "ListableBeanFactory must not be null");
374 Map beansOfType = lbf.getBeansOfType(type);
375 if (beansOfType.size() == 1) {
376 return beansOfType.values().iterator().next();
377 }
378 else {
379 throw new NoSuchBeanDefinitionException(type, "expected single bean but found " + beansOfType.size());
380 }
381 }
382
383 /**
384 * Return a single bean of the given type or subtypes, not looking in ancestor
385 * factories. Useful convenience method when we expect a single bean and
386 * don't care about the bean name.
387 * <p>Does consider objects created by FactoryBeans if the "allowEagerInit"
388 * flag is set, which means that FactoryBeans will get initialized. If the
389 * object created by the FactoryBean doesn't match, the raw FactoryBean itself
390 * will be matched against the type. If "allowEagerInit" is not set,
391 * only raw FactoryBeans will be checked (which doesn't require initialization
392 * of each FactoryBean).
393 * @param lbf the bean factory
394 * @param type type of bean to match
395 * @param includeNonSingletons whether to include prototype or scoped beans too
396 * or just singletons (also applies to FactoryBeans)
397 * @param allowEagerInit whether to initialize <i>lazy-init singletons</i> and
398 * <i>objects created by FactoryBeans</i> (or by factory methods with a
399 * "factory-bean" reference) for the type check. Note that FactoryBeans need to be
400 * eagerly initialized to determine their type: So be aware that passing in "true"
401 * for this flag will initialize FactoryBeans and "factory-bean" references.
402 * @return the matching bean instance
403 * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException
404 * if 0 or more than 1 beans of the given type were found
405 * @throws BeansException if the bean could not be created
406 */
407 public static Object beanOfType(
408 ListableBeanFactory lbf, Class type, boolean includeNonSingletons, boolean allowEagerInit)
409 throws BeansException {
410
411 Assert.notNull(lbf, "ListableBeanFactory must not be null");
412 Map beansOfType = lbf.getBeansOfType(type, includeNonSingletons, allowEagerInit);
413 if (beansOfType.size() == 1) {
414 return beansOfType.values().iterator().next();
415 }
416 else {
417 throw new NoSuchBeanDefinitionException(type, "expected single bean but found " + beansOfType.size());
418 }
419 }
420
421 }