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.config;
18  
19  import java.io.IOException;
20  import java.io.ObjectInputStream;
21  import java.io.Serializable;
22  import java.lang.annotation.Annotation;
23  import java.lang.reflect.Field;
24  import java.lang.reflect.ParameterizedType;
25  import java.lang.reflect.Type;
26  
27  import org.springframework.core.GenericCollectionTypeResolver;
28  import org.springframework.core.GenericTypeResolver;
29  import org.springframework.core.MethodParameter;
30  import org.springframework.core.ParameterNameDiscoverer;
31  import org.springframework.core.ResolvableType;
32  import org.springframework.util.Assert;
33  
34  /**
35   * Descriptor for a specific dependency that is about to be injected.
36   * Wraps a constructor parameter, a method parameter or a field,
37   * allowing unified access to their metadata.
38   *
39   * @author Juergen Hoeller
40   * @since 2.5
41   */
42  @SuppressWarnings("serial")
43  public class DependencyDescriptor implements Serializable {
44  
45  	private transient MethodParameter methodParameter;
46  
47  	private transient Field field;
48  
49  	private Class<?> declaringClass;
50  
51  	private Class<?> containingClass;
52  
53  	private String methodName;
54  
55  	private Class<?>[] parameterTypes;
56  
57  	private int parameterIndex;
58  
59  	private String fieldName;
60  
61  	private final boolean required;
62  
63  	private final boolean eager;
64  
65  	private int nestingLevel = 1;
66  
67  	private transient Annotation[] fieldAnnotations;
68  
69  
70  	/**
71  	 * Create a new descriptor for a method or constructor parameter.
72  	 * Considers the dependency as 'eager'.
73  	 * @param methodParameter the MethodParameter to wrap
74  	 * @param required whether the dependency is required
75  	 */
76  	public DependencyDescriptor(MethodParameter methodParameter, boolean required) {
77  		this(methodParameter, required, true);
78  	}
79  
80  	/**
81  	 * Create a new descriptor for a method or constructor parameter.
82  	 * @param methodParameter the MethodParameter to wrap
83  	 * @param required whether the dependency is required
84  	 * @param eager whether this dependency is 'eager' in the sense of
85  	 * eagerly resolving potential target beans for type matching
86  	 */
87  	public DependencyDescriptor(MethodParameter methodParameter, boolean required, boolean eager) {
88  		Assert.notNull(methodParameter, "MethodParameter must not be null");
89  		this.methodParameter = methodParameter;
90  		this.declaringClass = methodParameter.getDeclaringClass();
91  		this.containingClass = methodParameter.getContainingClass();
92  		if (this.methodParameter.getMethod() != null) {
93  			this.methodName = methodParameter.getMethod().getName();
94  			this.parameterTypes = methodParameter.getMethod().getParameterTypes();
95  		}
96  		else {
97  			this.parameterTypes = methodParameter.getConstructor().getParameterTypes();
98  		}
99  		this.parameterIndex = methodParameter.getParameterIndex();
100 		this.required = required;
101 		this.eager = eager;
102 	}
103 
104 	/**
105 	 * Create a new descriptor for a field.
106 	 * Considers the dependency as 'eager'.
107 	 * @param field the field to wrap
108 	 * @param required whether the dependency is required
109 	 */
110 	public DependencyDescriptor(Field field, boolean required) {
111 		this(field, required, true);
112 	}
113 
114 	/**
115 	 * Create a new descriptor for a field.
116 	 * @param field the field to wrap
117 	 * @param required whether the dependency is required
118 	 * @param eager whether this dependency is 'eager' in the sense of
119 	 * eagerly resolving potential target beans for type matching
120 	 */
121 	public DependencyDescriptor(Field field, boolean required, boolean eager) {
122 		Assert.notNull(field, "Field must not be null");
123 		this.field = field;
124 		this.declaringClass = field.getDeclaringClass();
125 		this.fieldName = field.getName();
126 		this.required = required;
127 		this.eager = eager;
128 	}
129 
130 	/**
131 	 * Copy constructor.
132 	 * @param original the original descriptor to create a copy from
133 	 */
134 	public DependencyDescriptor(DependencyDescriptor original) {
135 		this.methodParameter = (original.methodParameter != null ? new MethodParameter(original.methodParameter) : null);
136 		this.field = original.field;
137 		this.declaringClass = original.declaringClass;
138 		this.containingClass = original.containingClass;
139 		this.methodName = original.methodName;
140 		this.parameterTypes = original.parameterTypes;
141 		this.parameterIndex = original.parameterIndex;
142 		this.fieldName = original.fieldName;
143 		this.required = original.required;
144 		this.eager = original.eager;
145 		this.nestingLevel = original.nestingLevel;
146 		this.fieldAnnotations = original.fieldAnnotations;
147 	}
148 
149 
150 	/**
151 	 * Return the wrapped MethodParameter, if any.
152 	 * <p>Note: Either MethodParameter or Field is available.
153 	 * @return the MethodParameter, or {@code null} if none
154 	 */
155 	public MethodParameter getMethodParameter() {
156 		return this.methodParameter;
157 	}
158 
159 	/**
160 	 * Return the wrapped Field, if any.
161 	 * <p>Note: Either MethodParameter or Field is available.
162 	 * @return the Field, or {@code null} if none
163 	 */
164 	public Field getField() {
165 		return this.field;
166 	}
167 
168 	/**
169 	 * Return whether this dependency is required.
170 	 */
171 	public boolean isRequired() {
172 		return this.required;
173 	}
174 
175 	/**
176 	 * Return whether this dependency is 'eager' in the sense of
177 	 * eagerly resolving potential target beans for type matching.
178 	 */
179 	public boolean isEager() {
180 		return this.eager;
181 	}
182 
183 
184 	/**
185 	 * Increase this descriptor's nesting level.
186 	 * @see MethodParameter#increaseNestingLevel()
187 	 */
188 	public void increaseNestingLevel() {
189 		this.nestingLevel++;
190 		if (this.methodParameter != null) {
191 			this.methodParameter.increaseNestingLevel();
192 		}
193 	}
194 
195 	/**
196 	 * Optionally set the concrete class that contains this dependency.
197 	 * This may differ from the class that declares the parameter/field in that
198 	 * it may be a subclass thereof, potentially substituting type variables.
199 	 */
200 	public void setContainingClass(Class<?> containingClass) {
201 		this.containingClass = containingClass;
202 		if (this.methodParameter != null) {
203 			GenericTypeResolver.resolveParameterType(this.methodParameter, containingClass);
204 		}
205 	}
206 
207 	/**
208 	 * Build a ResolvableType object for the wrapped parameter/field.
209 	 */
210 	public ResolvableType getResolvableType() {
211 		return (this.field != null ? ResolvableType.forField(this.field, this.nestingLevel, this.containingClass) :
212 				ResolvableType.forMethodParameter(this.methodParameter));
213 	}
214 
215 	/**
216 	 * Return whether a fallback match is allowed.
217 	 * <p>This is {@code false} by default but may be overridden to return {@code true} in order
218 	 * to suggest to a {@link org.springframework.beans.factory.support.AutowireCandidateResolver}
219 	 * that a fallback match is acceptable as well.
220 	 */
221 	public boolean fallbackMatchAllowed() {
222 		return false;
223 	}
224 
225 	/**
226 	 * Return a variant of this descriptor that is intended for a fallback match.
227 	 * @see #fallbackMatchAllowed()
228 	 */
229 	public DependencyDescriptor forFallbackMatch() {
230 		return new DependencyDescriptor(this) {
231 			@Override
232 			public boolean fallbackMatchAllowed() {
233 				return true;
234 			}
235 		};
236 	}
237 
238 	/**
239 	 * Initialize parameter name discovery for the underlying method parameter, if any.
240 	 * <p>This method does not actually try to retrieve the parameter name at
241 	 * this point; it just allows discovery to happen when the application calls
242 	 * {@link #getDependencyName()} (if ever).
243 	 */
244 	public void initParameterNameDiscovery(ParameterNameDiscoverer parameterNameDiscoverer) {
245 		if (this.methodParameter != null) {
246 			this.methodParameter.initParameterNameDiscovery(parameterNameDiscoverer);
247 		}
248 	}
249 
250 	/**
251 	 * Determine the name of the wrapped parameter/field.
252 	 * @return the declared name (never {@code null})
253 	 */
254 	public String getDependencyName() {
255 		return (this.field != null ? this.field.getName() : this.methodParameter.getParameterName());
256 	}
257 
258 	/**
259 	 * Determine the declared (non-generic) type of the wrapped parameter/field.
260 	 * @return the declared type (never {@code null})
261 	 */
262 	public Class<?> getDependencyType() {
263 		if (this.field != null) {
264 			if (this.nestingLevel > 1) {
265 				Type type = this.field.getGenericType();
266 				for (int i = 2; i <= this.nestingLevel; i++) {
267 					if (type instanceof ParameterizedType) {
268 						Type[] args = ((ParameterizedType) type).getActualTypeArguments();
269 						type = args[args.length - 1];
270 					}
271 				}
272 				if (type instanceof Class) {
273 					return (Class<?>) type;
274 				}
275 				else if (type instanceof ParameterizedType) {
276 					Type arg = ((ParameterizedType) type).getRawType();
277 					if (arg instanceof Class) {
278 						return (Class<?>) arg;
279 					}
280 				}
281 				return Object.class;
282 			}
283 			else {
284 				return this.field.getType();
285 			}
286 		}
287 		else {
288 			return this.methodParameter.getNestedParameterType();
289 		}
290 	}
291 
292 	/**
293 	 * Determine the generic element type of the wrapped Collection parameter/field, if any.
294 	 * @return the generic type, or {@code null} if none
295 	 */
296 	public Class<?> getCollectionType() {
297 		return (this.field != null ?
298 				GenericCollectionTypeResolver.getCollectionFieldType(this.field, this.nestingLevel) :
299 				GenericCollectionTypeResolver.getCollectionParameterType(this.methodParameter));
300 	}
301 
302 	/**
303 	 * Determine the generic key type of the wrapped Map parameter/field, if any.
304 	 * @return the generic type, or {@code null} if none
305 	 */
306 	public Class<?> getMapKeyType() {
307 		return (this.field != null ?
308 				GenericCollectionTypeResolver.getMapKeyFieldType(this.field, this.nestingLevel) :
309 				GenericCollectionTypeResolver.getMapKeyParameterType(this.methodParameter));
310 	}
311 
312 	/**
313 	 * Determine the generic value type of the wrapped Map parameter/field, if any.
314 	 * @return the generic type, or {@code null} if none
315 	 */
316 	public Class<?> getMapValueType() {
317 		return (this.field != null ?
318 				GenericCollectionTypeResolver.getMapValueFieldType(this.field, this.nestingLevel) :
319 				GenericCollectionTypeResolver.getMapValueParameterType(this.methodParameter));
320 	}
321 
322 	/**
323 	 * Obtain the annotations associated with the wrapped parameter/field, if any.
324 	 */
325 	public Annotation[] getAnnotations() {
326 		if (this.field != null) {
327 			if (this.fieldAnnotations == null) {
328 				this.fieldAnnotations = this.field.getAnnotations();
329 			}
330 			return this.fieldAnnotations;
331 		}
332 		else {
333 			return this.methodParameter.getParameterAnnotations();
334 		}
335 	}
336 
337 
338 	//---------------------------------------------------------------------
339 	// Serialization support
340 	//---------------------------------------------------------------------
341 
342 	private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
343 		// Rely on default serialization; just initialize state after deserialization.
344 		ois.defaultReadObject();
345 
346 		// Restore reflective handles (which are unfortunately not serializable)
347 		try {
348 			if (this.fieldName != null) {
349 				this.field = this.declaringClass.getDeclaredField(this.fieldName);
350 			}
351 			else {
352 				if (this.methodName != null) {
353 					this.methodParameter = new MethodParameter(
354 							this.declaringClass.getDeclaredMethod(this.methodName, this.parameterTypes), this.parameterIndex);
355 				}
356 				else {
357 					this.methodParameter = new MethodParameter(
358 							this.declaringClass.getDeclaredConstructor(this.parameterTypes), this.parameterIndex);
359 				}
360 				for (int i = 1; i < this.nestingLevel; i++) {
361 					this.methodParameter.increaseNestingLevel();
362 				}
363 			}
364 		}
365 		catch (Throwable ex) {
366 			throw new IllegalStateException("Could not find original class structure", ex);
367 		}
368 	}
369 
370 }