View Javadoc
1   /*
2    * Copyright 2002-2014 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.lang.reflect.Array;
20  import java.util.ArrayList;
21  import java.util.LinkedHashMap;
22  import java.util.LinkedHashSet;
23  import java.util.List;
24  import java.util.Map;
25  import java.util.Properties;
26  import java.util.Set;
27  
28  import org.springframework.beans.BeanWrapper;
29  import org.springframework.beans.BeansException;
30  import org.springframework.beans.TypeConverter;
31  import org.springframework.beans.factory.BeanCreationException;
32  import org.springframework.beans.factory.BeanDefinitionStoreException;
33  import org.springframework.beans.factory.BeanFactoryUtils;
34  import org.springframework.beans.factory.FactoryBean;
35  import org.springframework.beans.factory.config.BeanDefinition;
36  import org.springframework.beans.factory.config.BeanDefinitionHolder;
37  import org.springframework.beans.factory.config.RuntimeBeanNameReference;
38  import org.springframework.beans.factory.config.RuntimeBeanReference;
39  import org.springframework.beans.factory.config.TypedStringValue;
40  import org.springframework.util.ClassUtils;
41  import org.springframework.util.ObjectUtils;
42  import org.springframework.util.StringUtils;
43  
44  /**
45   * Helper class for use in bean factory implementations,
46   * resolving values contained in bean definition objects
47   * into the actual values applied to the target bean instance.
48   *
49   * <p>Operates on an {@link AbstractBeanFactory} and a plain
50   * {@link org.springframework.beans.factory.config.BeanDefinition} object.
51   * Used by {@link AbstractAutowireCapableBeanFactory}.
52   *
53   * @author Juergen Hoeller
54   * @since 1.2
55   * @see AbstractAutowireCapableBeanFactory
56   */
57  class BeanDefinitionValueResolver {
58  
59  	private final AbstractBeanFactory beanFactory;
60  
61  	private final String beanName;
62  
63  	private final BeanDefinition beanDefinition;
64  
65  	private final TypeConverter typeConverter;
66  
67  
68  	/**
69  	 * Create a BeanDefinitionValueResolver for the given BeanFactory and BeanDefinition.
70  	 * @param beanFactory the BeanFactory to resolve against
71  	 * @param beanName the name of the bean that we work on
72  	 * @param beanDefinition the BeanDefinition of the bean that we work on
73  	 * @param typeConverter the TypeConverter to use for resolving TypedStringValues
74  	 */
75  	public BeanDefinitionValueResolver(
76  			AbstractBeanFactory beanFactory, String beanName, BeanDefinition beanDefinition, TypeConverter typeConverter) {
77  
78  		this.beanFactory = beanFactory;
79  		this.beanName = beanName;
80  		this.beanDefinition = beanDefinition;
81  		this.typeConverter = typeConverter;
82  	}
83  
84  
85  	/**
86  	 * Given a PropertyValue, return a value, resolving any references to other
87  	 * beans in the factory if necessary. The value could be:
88  	 * <li>A BeanDefinition, which leads to the creation of a corresponding
89  	 * new bean instance. Singleton flags and names of such "inner beans"
90  	 * are always ignored: Inner beans are anonymous prototypes.
91  	 * <li>A RuntimeBeanReference, which must be resolved.
92  	 * <li>A ManagedList. This is a special collection that may contain
93  	 * RuntimeBeanReferences or Collections that will need to be resolved.
94  	 * <li>A ManagedSet. May also contain RuntimeBeanReferences or
95  	 * Collections that will need to be resolved.
96  	 * <li>A ManagedMap. In this case the value may be a RuntimeBeanReference
97  	 * or Collection that will need to be resolved.
98  	 * <li>An ordinary object or {@code null}, in which case it's left alone.
99  	 * @param argName the name of the argument that the value is defined for
100 	 * @param value the value object to resolve
101 	 * @return the resolved object
102 	 */
103 	public Object resolveValueIfNecessary(Object argName, Object value) {
104 		// We must check each value to see whether it requires a runtime reference
105 		// to another bean to be resolved.
106 		if (value instanceof RuntimeBeanReference) {
107 			RuntimeBeanReference ref = (RuntimeBeanReference) value;
108 			return resolveReference(argName, ref);
109 		}
110 		else if (value instanceof RuntimeBeanNameReference) {
111 			String refName = ((RuntimeBeanNameReference) value).getBeanName();
112 			refName = String.valueOf(doEvaluate(refName));
113 			if (!this.beanFactory.containsBean(refName)) {
114 				throw new BeanDefinitionStoreException(
115 						"Invalid bean name '" + refName + "' in bean reference for " + argName);
116 			}
117 			return refName;
118 		}
119 		else if (value instanceof BeanDefinitionHolder) {
120 			// Resolve BeanDefinitionHolder: contains BeanDefinition with name and aliases.
121 			BeanDefinitionHolder bdHolder = (BeanDefinitionHolder) value;
122 			return resolveInnerBean(argName, bdHolder.getBeanName(), bdHolder.getBeanDefinition());
123 		}
124 		else if (value instanceof BeanDefinition) {
125 			// Resolve plain BeanDefinition, without contained name: use dummy name.
126 			BeanDefinition bd = (BeanDefinition) value;
127 			String innerBeanName = "(inner bean)" + BeanFactoryUtils.GENERATED_BEAN_NAME_SEPARATOR +
128 					ObjectUtils.getIdentityHexString(bd);
129 			return resolveInnerBean(argName, innerBeanName, bd);
130 		}
131 		else if (value instanceof ManagedArray) {
132 			// May need to resolve contained runtime references.
133 			ManagedArray array = (ManagedArray) value;
134 			Class<?> elementType = array.resolvedElementType;
135 			if (elementType == null) {
136 				String elementTypeName = array.getElementTypeName();
137 				if (StringUtils.hasText(elementTypeName)) {
138 					try {
139 						elementType = ClassUtils.forName(elementTypeName, this.beanFactory.getBeanClassLoader());
140 						array.resolvedElementType = elementType;
141 					}
142 					catch (Throwable ex) {
143 						// Improve the message by showing the context.
144 						throw new BeanCreationException(
145 								this.beanDefinition.getResourceDescription(), this.beanName,
146 								"Error resolving array type for " + argName, ex);
147 					}
148 				}
149 				else {
150 					elementType = Object.class;
151 				}
152 			}
153 			return resolveManagedArray(argName, (List<?>) value, elementType);
154 		}
155 		else if (value instanceof ManagedList) {
156 			// May need to resolve contained runtime references.
157 			return resolveManagedList(argName, (List<?>) value);
158 		}
159 		else if (value instanceof ManagedSet) {
160 			// May need to resolve contained runtime references.
161 			return resolveManagedSet(argName, (Set<?>) value);
162 		}
163 		else if (value instanceof ManagedMap) {
164 			// May need to resolve contained runtime references.
165 			return resolveManagedMap(argName, (Map<?, ?>) value);
166 		}
167 		else if (value instanceof ManagedProperties) {
168 			Properties original = (Properties) value;
169 			Properties copy = new Properties();
170 			for (Map.Entry<Object, Object> propEntry : original.entrySet()) {
171 				Object propKey = propEntry.getKey();
172 				Object propValue = propEntry.getValue();
173 				if (propKey instanceof TypedStringValue) {
174 					propKey = evaluate((TypedStringValue) propKey);
175 				}
176 				if (propValue instanceof TypedStringValue) {
177 					propValue = evaluate((TypedStringValue) propValue);
178 				}
179 				copy.put(propKey, propValue);
180 			}
181 			return copy;
182 		}
183 		else if (value instanceof TypedStringValue) {
184 			// Convert value to target type here.
185 			TypedStringValue typedStringValue = (TypedStringValue) value;
186 			Object valueObject = evaluate(typedStringValue);
187 			try {
188 				Class<?> resolvedTargetType = resolveTargetType(typedStringValue);
189 				if (resolvedTargetType != null) {
190 					return this.typeConverter.convertIfNecessary(valueObject, resolvedTargetType);
191 				}
192 				else {
193 					return valueObject;
194 				}
195 			}
196 			catch (Throwable ex) {
197 				// Improve the message by showing the context.
198 				throw new BeanCreationException(
199 						this.beanDefinition.getResourceDescription(), this.beanName,
200 						"Error converting typed String value for " + argName, ex);
201 			}
202 		}
203 		else {
204 			return evaluate(value);
205 		}
206 	}
207 
208 	/**
209 	 * Evaluate the given value as an expression, if necessary.
210 	 * @param value the candidate value (may be an expression)
211 	 * @return the resolved value
212 	 */
213 	protected Object evaluate(TypedStringValue value) {
214 		Object result = doEvaluate(value.getValue());
215 		if (!ObjectUtils.nullSafeEquals(result, value.getValue())) {
216 			value.setDynamic();
217 		}
218 		return result;
219 	}
220 
221 	/**
222 	 * Evaluate the given value as an expression, if necessary.
223 	 * @param value the original value (may be an expression)
224 	 * @return the resolved value if necessary, or the original value
225 	 */
226 	protected Object evaluate(Object value) {
227 		if (value instanceof String) {
228 			return doEvaluate((String) value);
229 		}
230 		else if (value instanceof String[]) {
231 			String[] values = (String[]) value;
232 			boolean actuallyResolved = false;
233 			Object[] resolvedValues = new Object[values.length];
234 			for (int i = 0; i < values.length; i++) {
235 				String originalValue = values[i];
236 				Object resolvedValue = doEvaluate(originalValue);
237 				if (resolvedValue != originalValue) {
238 					actuallyResolved = true;
239 				}
240 				resolvedValues[i] = resolvedValue;
241 			}
242 			return (actuallyResolved ? resolvedValues : values);
243 		}
244 		else {
245 			return value;
246 		}
247 	}
248 
249 	/**
250 	 * Evaluate the given String value as an expression, if necessary.
251 	 * @param value the original value (may be an expression)
252 	 * @return the resolved value if necessary, or the original String value
253 	 */
254 	private Object doEvaluate(String value) {
255 		return this.beanFactory.evaluateBeanDefinitionString(value, this.beanDefinition);
256 	}
257 
258 	/**
259 	 * Resolve the target type in the given TypedStringValue.
260 	 * @param value the TypedStringValue to resolve
261 	 * @return the resolved target type (or {@code null} if none specified)
262 	 * @throws ClassNotFoundException if the specified type cannot be resolved
263 	 * @see TypedStringValue#resolveTargetType
264 	 */
265 	protected Class<?> resolveTargetType(TypedStringValue value) throws ClassNotFoundException {
266 		if (value.hasTargetType()) {
267 			return value.getTargetType();
268 		}
269 		return value.resolveTargetType(this.beanFactory.getBeanClassLoader());
270 	}
271 
272 	/**
273 	 * Resolve an inner bean definition.
274 	 * @param argName the name of the argument that the inner bean is defined for
275 	 * @param innerBeanName the name of the inner bean
276 	 * @param innerBd the bean definition for the inner bean
277 	 * @return the resolved inner bean instance
278 	 */
279 	private Object resolveInnerBean(Object argName, String innerBeanName, BeanDefinition innerBd) {
280 		RootBeanDefinition mbd = null;
281 		try {
282 			mbd = this.beanFactory.getMergedBeanDefinition(innerBeanName, innerBd, this.beanDefinition);
283 			// Check given bean name whether it is unique. If not already unique,
284 			// add counter - increasing the counter until the name is unique.
285 			String actualInnerBeanName = innerBeanName;
286 			if (mbd.isSingleton()) {
287 				actualInnerBeanName = adaptInnerBeanName(innerBeanName);
288 			}
289 			this.beanFactory.registerContainedBean(actualInnerBeanName, this.beanName);
290 			// Guarantee initialization of beans that the inner bean depends on.
291 			String[] dependsOn = mbd.getDependsOn();
292 			if (dependsOn != null) {
293 				for (String dependsOnBean : dependsOn) {
294 					this.beanFactory.registerDependentBean(dependsOnBean, actualInnerBeanName);
295 					this.beanFactory.getBean(dependsOnBean);
296 				}
297 			}
298 			// Actually create the inner bean instance now...
299 			Object innerBean = this.beanFactory.createBean(actualInnerBeanName, mbd, null);
300 			if (innerBean instanceof FactoryBean) {
301 				boolean synthetic = mbd.isSynthetic();
302 				return this.beanFactory.getObjectFromFactoryBean(
303 						(FactoryBean<?>) innerBean, actualInnerBeanName, !synthetic);
304 			}
305 			else {
306 				return innerBean;
307 			}
308 		}
309 		catch (BeansException ex) {
310 			throw new BeanCreationException(
311 					this.beanDefinition.getResourceDescription(), this.beanName,
312 					"Cannot create inner bean '" + innerBeanName + "' " +
313 					(mbd != null && mbd.getBeanClassName() != null ? "of type [" + mbd.getBeanClassName() + "] " : "") +
314 					"while setting " + argName, ex);
315 		}
316 	}
317 
318 	/**
319 	 * Checks the given bean name whether it is unique. If not already unique,
320 	 * a counter is added, increasing the counter until the name is unique.
321 	 * @param innerBeanName the original name for the inner bean
322 	 * @return the adapted name for the inner bean
323 	 */
324 	private String adaptInnerBeanName(String innerBeanName) {
325 		String actualInnerBeanName = innerBeanName;
326 		int counter = 0;
327 		while (this.beanFactory.isBeanNameInUse(actualInnerBeanName)) {
328 			counter++;
329 			actualInnerBeanName = innerBeanName + BeanFactoryUtils.GENERATED_BEAN_NAME_SEPARATOR + counter;
330 		}
331 		return actualInnerBeanName;
332 	}
333 
334 	/**
335 	 * Resolve a reference to another bean in the factory.
336 	 */
337 	private Object resolveReference(Object argName, RuntimeBeanReference ref) {
338 		try {
339 			String refName = ref.getBeanName();
340 			refName = String.valueOf(doEvaluate(refName));
341 			if (ref.isToParent()) {
342 				if (this.beanFactory.getParentBeanFactory() == null) {
343 					throw new BeanCreationException(
344 							this.beanDefinition.getResourceDescription(), this.beanName,
345 							"Can't resolve reference to bean '" + refName +
346 							"' in parent factory: no parent factory available");
347 				}
348 				return this.beanFactory.getParentBeanFactory().getBean(refName);
349 			}
350 			else {
351 				Object bean = this.beanFactory.getBean(refName);
352 				this.beanFactory.registerDependentBean(refName, this.beanName);
353 				return bean;
354 			}
355 		}
356 		catch (BeansException ex) {
357 			throw new BeanCreationException(
358 					this.beanDefinition.getResourceDescription(), this.beanName,
359 					"Cannot resolve reference to bean '" + ref.getBeanName() + "' while setting " + argName, ex);
360 		}
361 	}
362 
363 	/**
364 	 * For each element in the managed array, resolve reference if necessary.
365 	 */
366 	private Object resolveManagedArray(Object argName, List<?> ml, Class<?> elementType) {
367 		Object resolved = Array.newInstance(elementType, ml.size());
368 		for (int i = 0; i < ml.size(); i++) {
369 			Array.set(resolved, i,
370 					resolveValueIfNecessary(new KeyedArgName(argName, i), ml.get(i)));
371 		}
372 		return resolved;
373 	}
374 
375 	/**
376 	 * For each element in the managed list, resolve reference if necessary.
377 	 */
378 	private List<?> resolveManagedList(Object argName, List<?> ml) {
379 		List<Object> resolved = new ArrayList<Object>(ml.size());
380 		for (int i = 0; i < ml.size(); i++) {
381 			resolved.add(
382 					resolveValueIfNecessary(new KeyedArgName(argName, i), ml.get(i)));
383 		}
384 		return resolved;
385 	}
386 
387 	/**
388 	 * For each element in the managed set, resolve reference if necessary.
389 	 */
390 	private Set<?> resolveManagedSet(Object argName, Set<?> ms) {
391 		Set<Object> resolved = new LinkedHashSet<Object>(ms.size());
392 		int i = 0;
393 		for (Object m : ms) {
394 			resolved.add(resolveValueIfNecessary(new KeyedArgName(argName, i), m));
395 			i++;
396 		}
397 		return resolved;
398 	}
399 
400 	/**
401 	 * For each element in the managed map, resolve reference if necessary.
402 	 */
403 	private Map<?, ?> resolveManagedMap(Object argName, Map<?, ?> mm) {
404 		Map<Object, Object> resolved = new LinkedHashMap<Object, Object>(mm.size());
405 		for (Map.Entry<?, ?> entry : mm.entrySet()) {
406 			Object resolvedKey = resolveValueIfNecessary(argName, entry.getKey());
407 			Object resolvedValue = resolveValueIfNecessary(
408 					new KeyedArgName(argName, entry.getKey()), entry.getValue());
409 			resolved.put(resolvedKey, resolvedValue);
410 		}
411 		return resolved;
412 	}
413 
414 
415 	/**
416 	 * Holder class used for delayed toString building.
417 	 */
418 	private static class KeyedArgName {
419 
420 		private final Object argName;
421 
422 		private final Object key;
423 
424 		public KeyedArgName(Object argName, Object key) {
425 			this.argName = argName;
426 			this.key = key;
427 		}
428 
429 		@Override
430 		public String toString() {
431 			return this.argName + " with key " + BeanWrapper.PROPERTY_KEY_PREFIX +
432 					this.key + BeanWrapper.PROPERTY_KEY_SUFFIX;
433 		}
434 	}
435 
436 }