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.support;
18
19 import java.util.ArrayList;
20 import java.util.Iterator;
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
41 /**
42 * Helper class for use in bean factory implementations,
43 * resolving values contained in bean definition objects
44 * into the actual values applied to the target bean instance.
45 *
46 * <p>Operates on an {@link AbstractBeanFactory} and a plain
47 * {@link org.springframework.beans.factory.config.BeanDefinition} object.
48 * Used by {@link AbstractAutowireCapableBeanFactory}.
49 *
50 * @author Juergen Hoeller
51 * @since 1.2
52 * @see AbstractAutowireCapableBeanFactory
53 */
54 class BeanDefinitionValueResolver {
55
56 private final AbstractBeanFactory beanFactory;
57
58 private final String beanName;
59
60 private final BeanDefinition beanDefinition;
61
62 private final TypeConverter typeConverter;
63
64
65 /**
66 * Create a BeanDefinitionValueResolver for the given BeanFactory and BeanDefinition.
67 * @param beanFactory the BeanFactory to resolve against
68 * @param beanName the name of the bean that we work on
69 * @param beanDefinition the BeanDefinition of the bean that we work on
70 * @param typeConverter the TypeConverter to use for resolving TypedStringValues
71 */
72 public BeanDefinitionValueResolver(
73 AbstractBeanFactory beanFactory, String beanName, BeanDefinition beanDefinition, TypeConverter typeConverter) {
74
75 this.beanFactory = beanFactory;
76 this.beanName = beanName;
77 this.beanDefinition = beanDefinition;
78 this.typeConverter = typeConverter;
79 }
80
81 /**
82 * Given a PropertyValue, return a value, resolving any references to other
83 * beans in the factory if necessary. The value could be:
84 * <li>A BeanDefinition, which leads to the creation of a corresponding
85 * new bean instance. Singleton flags and names of such "inner beans"
86 * are always ignored: Inner beans are anonymous prototypes.
87 * <li>A RuntimeBeanReference, which must be resolved.
88 * <li>A ManagedList. This is a special collection that may contain
89 * RuntimeBeanReferences or Collections that will need to be resolved.
90 * <li>A ManagedSet. May also contain RuntimeBeanReferences or
91 * Collections that will need to be resolved.
92 * <li>A ManagedMap. In this case the value may be a RuntimeBeanReference
93 * or Collection that will need to be resolved.
94 * <li>An ordinary object or <code>null</code>, in which case it's left alone.
95 * @param argName the name of the argument that the value is defined for
96 * @param value the value object to resolve
97 * @return the resolved object
98 */
99 public Object resolveValueIfNecessary(Object argName, Object value) {
100 // We must check each value to see whether it requires a runtime reference
101 // to another bean to be resolved.
102 if (value instanceof RuntimeBeanReference) {
103 RuntimeBeanReference ref = (RuntimeBeanReference) value;
104 return resolveReference(argName, ref);
105 }
106 else if (value instanceof RuntimeBeanNameReference) {
107 String ref = ((RuntimeBeanNameReference) value).getBeanName();
108 if (!this.beanFactory.containsBean(ref)) {
109 throw new BeanDefinitionStoreException(
110 "Invalid bean name '" + ref + "' in bean reference for " + argName);
111 }
112 return ref;
113 }
114 else if (value instanceof BeanDefinitionHolder) {
115 // Resolve BeanDefinitionHolder: contains BeanDefinition with name and aliases.
116 BeanDefinitionHolder bdHolder = (BeanDefinitionHolder) value;
117 return resolveInnerBean(argName, bdHolder.getBeanName(), bdHolder.getBeanDefinition());
118 }
119 else if (value instanceof BeanDefinition) {
120 // Resolve plain BeanDefinition, without contained name: use dummy name.
121 BeanDefinition bd = (BeanDefinition) value;
122 return resolveInnerBean(argName, "(inner bean)", bd);
123 }
124 else if (value instanceof ManagedList) {
125 // May need to resolve contained runtime references.
126 return resolveManagedList(argName, (List) value);
127 }
128 else if (value instanceof ManagedSet) {
129 // May need to resolve contained runtime references.
130 return resolveManagedSet(argName, (Set) value);
131 }
132 else if (value instanceof ManagedMap) {
133 // May need to resolve contained runtime references.
134 return resolveManagedMap(argName, (Map) value);
135 }
136 else if (value instanceof ManagedProperties) {
137 Properties original = (Properties) value;
138 Properties copy = new Properties();
139 for (Iterator it = original.entrySet().iterator(); it.hasNext();) {
140 Map.Entry propEntry = (Map.Entry) it.next();
141 Object propKey = propEntry.getKey();
142 Object propValue = propEntry.getValue();
143 if (propKey instanceof TypedStringValue) {
144 propKey = ((TypedStringValue) propKey).getValue();
145 }
146 if (propValue instanceof TypedStringValue) {
147 propValue = ((TypedStringValue) propValue).getValue();
148 }
149 copy.put(propKey, propValue);
150 }
151 return copy;
152 }
153 else if (value instanceof TypedStringValue) {
154 // Convert value to target type here.
155 TypedStringValue typedStringValue = (TypedStringValue) value;
156 try {
157 Class resolvedTargetType = resolveTargetType(typedStringValue);
158 if (resolvedTargetType != null) {
159 return this.typeConverter.convertIfNecessary(typedStringValue.getValue(), resolvedTargetType);
160 }
161 else {
162 // No target type specified - no conversion necessary...
163 return typedStringValue.getValue();
164 }
165 }
166 catch (Throwable ex) {
167 // Improve the message by showing the context.
168 throw new BeanCreationException(
169 this.beanDefinition.getResourceDescription(), this.beanName,
170 "Error converting typed String value for " + argName, ex);
171 }
172 }
173 else {
174 // No need to resolve value...
175 return value;
176 }
177 }
178
179 /**
180 * Resolve the target type in the given TypedStringValue.
181 * @param value the TypedStringValue to resolve
182 * @return the resolved target type (or <code>null</code> if none specified)
183 * @throws ClassNotFoundException if the specified type cannot be resolved
184 * @see TypedStringValue#resolveTargetType
185 */
186 protected Class resolveTargetType(TypedStringValue value) throws ClassNotFoundException {
187 if (value.hasTargetType()) {
188 return value.getTargetType();
189 }
190 return value.resolveTargetType(this.beanFactory.getBeanClassLoader());
191 }
192
193 /**
194 * Resolve an inner bean definition.
195 * @param argName the name of the argument that the inner bean is defined for
196 * @param innerBeanName the name of the inner bean
197 * @param innerBd the bean definition for the inner bean
198 * @return the resolved inner bean instance
199 */
200 private Object resolveInnerBean(Object argName, String innerBeanName, BeanDefinition innerBd) {
201 RootBeanDefinition mbd = null;
202 try {
203 mbd = this.beanFactory.getMergedBeanDefinition(innerBeanName, innerBd, this.beanDefinition);
204 // Check given bean name whether it is unique. If not already unique,
205 // add counter - increasing the counter until the name is unique.
206 String actualInnerBeanName = innerBeanName;
207 if (mbd.isSingleton()) {
208 actualInnerBeanName = adaptInnerBeanName(innerBeanName);
209 }
210 // Guarantee initialization of beans that the inner bean depends on.
211 String[] dependsOn = mbd.getDependsOn();
212 if (dependsOn != null) {
213 for (int i = 0; i < dependsOn.length; i++) {
214 String dependsOnBean = dependsOn[i];
215 this.beanFactory.getBean(dependsOnBean);
216 this.beanFactory.registerDependentBean(dependsOnBean, actualInnerBeanName);
217 }
218 }
219 Object innerBean = this.beanFactory.createBean(actualInnerBeanName, mbd, null);
220 this.beanFactory.registerContainedBean(actualInnerBeanName, this.beanName);
221 if (innerBean instanceof FactoryBean) {
222 boolean synthetic = (mbd != null && mbd.isSynthetic());
223 return this.beanFactory.getObjectFromFactoryBean((FactoryBean) innerBean, actualInnerBeanName, !synthetic);
224 }
225 else {
226 return innerBean;
227 }
228 }
229 catch (BeansException ex) {
230 throw new BeanCreationException(
231 this.beanDefinition.getResourceDescription(), this.beanName,
232 "Cannot create inner bean '" + innerBeanName + "' " +
233 (mbd != null && mbd.getBeanClassName() != null ? "of type [" + mbd.getBeanClassName() + "] " : "") +
234 "while setting " + argName, ex);
235 }
236 }
237
238 /**
239 * Checks the given bean name whether it is unique. If not already unique,
240 * a counter is added, increasing the counter until the name is unique.
241 * @param innerBeanName the original name for the inner bean
242 * @return the adapted name for the inner bean
243 */
244 private String adaptInnerBeanName(String innerBeanName) {
245 String actualInnerBeanName = innerBeanName;
246 int counter = 0;
247 while (this.beanFactory.isBeanNameInUse(actualInnerBeanName)) {
248 counter++;
249 actualInnerBeanName = innerBeanName + BeanFactoryUtils.GENERATED_BEAN_NAME_SEPARATOR + counter;
250 }
251 return actualInnerBeanName;
252 }
253
254 /**
255 * Resolve a reference to another bean in the factory.
256 */
257 private Object resolveReference(Object argName, RuntimeBeanReference ref) {
258 try {
259 if (ref.isToParent()) {
260 if (this.beanFactory.getParentBeanFactory() == null) {
261 throw new BeanCreationException(
262 this.beanDefinition.getResourceDescription(), this.beanName,
263 "Can't resolve reference to bean '" + ref.getBeanName() +
264 "' in parent factory: no parent factory available");
265 }
266 return this.beanFactory.getParentBeanFactory().getBean(ref.getBeanName());
267 }
268 else {
269 Object bean = this.beanFactory.getBean(ref.getBeanName());
270 this.beanFactory.registerDependentBean(ref.getBeanName(), this.beanName);
271 return bean;
272 }
273 }
274 catch (BeansException ex) {
275 throw new BeanCreationException(
276 this.beanDefinition.getResourceDescription(), this.beanName,
277 "Cannot resolve reference to bean '" + ref.getBeanName() + "' while setting " + argName, ex);
278 }
279 }
280
281 /**
282 * For each element in the ManagedList, resolve reference if necessary.
283 */
284 private List resolveManagedList(Object argName, List ml) {
285 List resolved = new ArrayList(ml.size());
286 for (int i = 0; i < ml.size(); i++) {
287 resolved.add(
288 resolveValueIfNecessary(
289 argName + " with key " + BeanWrapper.PROPERTY_KEY_PREFIX + i + BeanWrapper.PROPERTY_KEY_SUFFIX,
290 ml.get(i)));
291 }
292 return resolved;
293 }
294
295 /**
296 * For each element in the ManagedList, resolve reference if necessary.
297 */
298 private Set resolveManagedSet(Object argName, Set ms) {
299 Set resolved = new LinkedHashSet(ms.size());
300 int i = 0;
301 for (Iterator it = ms.iterator(); it.hasNext();) {
302 resolved.add(
303 resolveValueIfNecessary(
304 argName + " with key " + BeanWrapper.PROPERTY_KEY_PREFIX + i + BeanWrapper.PROPERTY_KEY_SUFFIX,
305 it.next()));
306 i++;
307 }
308 return resolved;
309 }
310
311 /**
312 * For each element in the ManagedMap, resolve reference if necessary.
313 */
314 private Map resolveManagedMap(Object argName, Map mm) {
315 Map resolved = new LinkedHashMap(mm.size());
316 Iterator it = mm.entrySet().iterator();
317 while (it.hasNext()) {
318 Map.Entry entry = (Map.Entry) it.next();
319 Object resolvedKey = resolveValueIfNecessary(argName, entry.getKey());
320 Object resolvedValue = resolveValueIfNecessary(
321 argName + " with key " + BeanWrapper.PROPERTY_KEY_PREFIX + entry.getKey() + BeanWrapper.PROPERTY_KEY_SUFFIX,
322 entry.getValue());
323 resolved.put(resolvedKey, resolvedValue);
324 }
325 return resolved;
326 }
327
328 }