001/*
002 * Copyright 2002-2019 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 *      https://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
017package org.springframework.beans.factory.config;
018
019import java.io.IOException;
020import java.io.ObjectInputStream;
021import java.io.Serializable;
022import java.lang.reflect.Field;
023import java.lang.reflect.ParameterizedType;
024import java.lang.reflect.Type;
025import java.util.Map;
026
027import org.springframework.beans.BeansException;
028import org.springframework.beans.factory.BeanFactory;
029import org.springframework.beans.factory.InjectionPoint;
030import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
031import org.springframework.core.GenericTypeResolver;
032import org.springframework.core.MethodParameter;
033import org.springframework.core.ParameterNameDiscoverer;
034import org.springframework.core.ResolvableType;
035
036/**
037 * Descriptor for a specific dependency that is about to be injected.
038 * Wraps a constructor parameter, a method parameter or a field,
039 * allowing unified access to their metadata.
040 *
041 * @author Juergen Hoeller
042 * @since 2.5
043 */
044@SuppressWarnings("serial")
045public class DependencyDescriptor extends InjectionPoint implements Serializable {
046
047        private final Class<?> declaringClass;
048
049        private String methodName;
050
051        private Class<?>[] parameterTypes;
052
053        private int parameterIndex;
054
055        private String fieldName;
056
057        private final boolean required;
058
059        private final boolean eager;
060
061        private int nestingLevel = 1;
062
063        private Class<?> containingClass;
064
065        private volatile ResolvableType resolvableType;
066
067
068        /**
069         * Create a new descriptor for a method or constructor parameter.
070         * Considers the dependency as 'eager'.
071         * @param methodParameter the MethodParameter to wrap
072         * @param required whether the dependency is required
073         */
074        public DependencyDescriptor(MethodParameter methodParameter, boolean required) {
075                this(methodParameter, required, true);
076        }
077
078        /**
079         * Create a new descriptor for a method or constructor parameter.
080         * @param methodParameter the MethodParameter to wrap
081         * @param required whether the dependency is required
082         * @param eager whether this dependency is 'eager' in the sense of
083         * eagerly resolving potential target beans for type matching
084         */
085        public DependencyDescriptor(MethodParameter methodParameter, boolean required, boolean eager) {
086                super(methodParameter);
087
088                this.declaringClass = methodParameter.getDeclaringClass();
089                if (methodParameter.getMethod() != null) {
090                        this.methodName = methodParameter.getMethod().getName();
091                        this.parameterTypes = methodParameter.getMethod().getParameterTypes();
092                }
093                else {
094                        this.parameterTypes = methodParameter.getConstructor().getParameterTypes();
095                }
096                this.parameterIndex = methodParameter.getParameterIndex();
097                this.containingClass = methodParameter.getContainingClass();
098                this.required = required;
099                this.eager = eager;
100        }
101
102        /**
103         * Create a new descriptor for a field.
104         * Considers the dependency as 'eager'.
105         * @param field the field to wrap
106         * @param required whether the dependency is required
107         */
108        public DependencyDescriptor(Field field, boolean required) {
109                this(field, required, true);
110        }
111
112        /**
113         * Create a new descriptor for a field.
114         * @param field the field to wrap
115         * @param required whether the dependency is required
116         * @param eager whether this dependency is 'eager' in the sense of
117         * eagerly resolving potential target beans for type matching
118         */
119        public DependencyDescriptor(Field field, boolean required, boolean eager) {
120                super(field);
121
122                this.declaringClass = field.getDeclaringClass();
123                this.fieldName = field.getName();
124                this.required = required;
125                this.eager = eager;
126        }
127
128        /**
129         * Copy constructor.
130         * @param original the original descriptor to create a copy from
131         */
132        public DependencyDescriptor(DependencyDescriptor original) {
133                super(original);
134
135                this.declaringClass = original.declaringClass;
136                this.methodName = original.methodName;
137                this.parameterTypes = original.parameterTypes;
138                this.parameterIndex = original.parameterIndex;
139                this.fieldName = original.fieldName;
140                this.containingClass = original.containingClass;
141                this.required = original.required;
142                this.eager = original.eager;
143                this.nestingLevel = original.nestingLevel;
144        }
145
146
147        /**
148         * Return whether this dependency is required.
149         */
150        public boolean isRequired() {
151                return this.required;
152        }
153
154        /**
155         * Return whether this dependency is 'eager' in the sense of
156         * eagerly resolving potential target beans for type matching.
157         */
158        public boolean isEager() {
159                return this.eager;
160        }
161
162        /**
163         * Resolve the specified not-unique scenario: by default,
164         * throwing a {@link NoUniqueBeanDefinitionException}.
165         * <p>Subclasses may override this to select one of the instances or
166         * to opt out with no result at all through returning {@code null}.
167         * @param type the requested bean type
168         * @param matchingBeans a map of bean names and corresponding bean
169         * instances which have been pre-selected for the given type
170         * (qualifiers etc already applied)
171         * @return a bean instance to proceed with, or {@code null} for none
172         * @throws BeansException in case of the not-unique scenario being fatal
173         * @since 4.3
174         */
175        public Object resolveNotUnique(Class<?> type, Map<String, Object> matchingBeans) throws BeansException {
176                throw new NoUniqueBeanDefinitionException(type, matchingBeans.keySet());
177        }
178
179        /**
180         * Resolve a shortcut for this dependency against the given factory, for example
181         * taking some pre-resolved information into account.
182         * <p>The resolution algorithm will first attempt to resolve a shortcut through this
183         * method before going into the regular type matching algorithm across all beans.
184         * Subclasses may override this method to improve resolution performance based on
185         * pre-cached information while still receiving {@link InjectionPoint} exposure etc.
186         * @param beanFactory the associated factory
187         * @return the shortcut result if any, or {@code null} if none
188         * @throws BeansException if the shortcut could not be obtained
189         * @since 4.3.1
190         */
191        public Object resolveShortcut(BeanFactory beanFactory) throws BeansException {
192                return null;
193        }
194
195        /**
196         * Resolve the specified bean name, as a candidate result of the matching
197         * algorithm for this dependency, to a bean instance from the given factory.
198         * <p>The default implementation calls {@link BeanFactory#getBean(String)}.
199         * Subclasses may provide additional arguments or other customizations.
200         * @param beanName the bean name, as a candidate result for this dependency
201         * @param requiredType the expected type of the bean (as an assertion)
202         * @param beanFactory the associated factory
203         * @return the bean instance (never {@code null})
204         * @throws BeansException if the bean could not be obtained
205         * @since 4.3.2
206         * @see BeanFactory#getBean(String)
207         */
208        public Object resolveCandidate(String beanName, Class<?> requiredType, BeanFactory beanFactory)
209                        throws BeansException {
210
211                return beanFactory.getBean(beanName, requiredType);
212        }
213
214
215        /**
216         * Increase this descriptor's nesting level.
217         * @see MethodParameter#increaseNestingLevel()
218         */
219        public void increaseNestingLevel() {
220                this.nestingLevel++;
221                this.resolvableType = null;
222                if (this.methodParameter != null) {
223                        this.methodParameter.increaseNestingLevel();
224                }
225        }
226
227        /**
228         * Optionally set the concrete class that contains this dependency.
229         * This may differ from the class that declares the parameter/field in that
230         * it may be a subclass thereof, potentially substituting type variables.
231         * @since 4.0
232         */
233        public void setContainingClass(Class<?> containingClass) {
234                this.containingClass = containingClass;
235                this.resolvableType = null;
236                if (this.methodParameter != null) {
237                        GenericTypeResolver.resolveParameterType(this.methodParameter, containingClass);
238                }
239        }
240
241        /**
242         * Build a {@link ResolvableType} object for the wrapped parameter/field.
243         * @since 4.0
244         */
245        public ResolvableType getResolvableType() {
246                ResolvableType resolvableType = this.resolvableType;
247                if (resolvableType == null) {
248                        resolvableType = (this.field != null ?
249                                        ResolvableType.forField(this.field, this.nestingLevel, this.containingClass) :
250                                        ResolvableType.forMethodParameter(this.methodParameter));
251                        this.resolvableType = resolvableType;
252                }
253                return resolvableType;
254        }
255
256        /**
257         * Return whether a fallback match is allowed.
258         * <p>This is {@code false} by default but may be overridden to return {@code true} in order
259         * to suggest to an {@link org.springframework.beans.factory.support.AutowireCandidateResolver}
260         * that a fallback match is acceptable as well.
261         * @since 4.0
262         */
263        public boolean fallbackMatchAllowed() {
264                return false;
265        }
266
267        /**
268         * Return a variant of this descriptor that is intended for a fallback match.
269         * @since 4.0
270         * @see #fallbackMatchAllowed()
271         */
272        public DependencyDescriptor forFallbackMatch() {
273                return new DependencyDescriptor(this) {
274                        @Override
275                        public boolean fallbackMatchAllowed() {
276                                return true;
277                        }
278                };
279        }
280
281        /**
282         * Initialize parameter name discovery for the underlying method parameter, if any.
283         * <p>This method does not actually try to retrieve the parameter name at
284         * this point; it just allows discovery to happen when the application calls
285         * {@link #getDependencyName()} (if ever).
286         */
287        public void initParameterNameDiscovery(ParameterNameDiscoverer parameterNameDiscoverer) {
288                if (this.methodParameter != null) {
289                        this.methodParameter.initParameterNameDiscovery(parameterNameDiscoverer);
290                }
291        }
292
293        /**
294         * Determine the name of the wrapped parameter/field.
295         * @return the declared name (never {@code null})
296         */
297        public String getDependencyName() {
298                return (this.field != null ? this.field.getName() : this.methodParameter.getParameterName());
299        }
300
301        /**
302         * Determine the declared (non-generic) type of the wrapped parameter/field.
303         * @return the declared type (never {@code null})
304         */
305        public Class<?> getDependencyType() {
306                if (this.field != null) {
307                        if (this.nestingLevel > 1) {
308                                Type type = this.field.getGenericType();
309                                for (int i = 2; i <= this.nestingLevel; i++) {
310                                        if (type instanceof ParameterizedType) {
311                                                Type[] args = ((ParameterizedType) type).getActualTypeArguments();
312                                                type = args[args.length - 1];
313                                        }
314                                }
315                                if (type instanceof Class) {
316                                        return (Class<?>) type;
317                                }
318                                else if (type instanceof ParameterizedType) {
319                                        Type arg = ((ParameterizedType) type).getRawType();
320                                        if (arg instanceof Class) {
321                                                return (Class<?>) arg;
322                                        }
323                                }
324                                return Object.class;
325                        }
326                        else {
327                                return this.field.getType();
328                        }
329                }
330                else {
331                        return this.methodParameter.getNestedParameterType();
332                }
333        }
334
335        /**
336         * Determine the generic element type of the wrapped Collection parameter/field, if any.
337         * @return the generic type, or {@code null} if none
338         * @deprecated as of 4.3.6, in favor of direct {@link ResolvableType} usage
339         */
340        @Deprecated
341        public Class<?> getCollectionType() {
342                return (this.field != null ?
343                                org.springframework.core.GenericCollectionTypeResolver.getCollectionFieldType(this.field, this.nestingLevel) :
344                                org.springframework.core.GenericCollectionTypeResolver.getCollectionParameterType(this.methodParameter));
345        }
346
347        /**
348         * Determine the generic key type of the wrapped Map parameter/field, if any.
349         * @return the generic type, or {@code null} if none
350         * @deprecated as of 4.3.6, in favor of direct {@link ResolvableType} usage
351         */
352        @Deprecated
353        public Class<?> getMapKeyType() {
354                return (this.field != null ?
355                                org.springframework.core.GenericCollectionTypeResolver.getMapKeyFieldType(this.field, this.nestingLevel) :
356                                org.springframework.core.GenericCollectionTypeResolver.getMapKeyParameterType(this.methodParameter));
357        }
358
359        /**
360         * Determine the generic value type of the wrapped Map parameter/field, if any.
361         * @return the generic type, or {@code null} if none
362         * @deprecated as of 4.3.6, in favor of direct {@link ResolvableType} usage
363         */
364        @Deprecated
365        public Class<?> getMapValueType() {
366                return (this.field != null ?
367                                org.springframework.core.GenericCollectionTypeResolver.getMapValueFieldType(this.field, this.nestingLevel) :
368                                org.springframework.core.GenericCollectionTypeResolver.getMapValueParameterType(this.methodParameter));
369        }
370
371
372        @Override
373        public boolean equals(Object other) {
374                if (this == other) {
375                        return true;
376                }
377                if (!super.equals(other)) {
378                        return false;
379                }
380                DependencyDescriptor otherDesc = (DependencyDescriptor) other;
381                return (this.required == otherDesc.required && this.eager == otherDesc.eager &&
382                                this.nestingLevel == otherDesc.nestingLevel && this.containingClass == otherDesc.containingClass);
383        }
384
385
386        //---------------------------------------------------------------------
387        // Serialization support
388        //---------------------------------------------------------------------
389
390        private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
391                // Rely on default serialization; just initialize state after deserialization.
392                ois.defaultReadObject();
393
394                // Restore reflective handles (which are unfortunately not serializable)
395                try {
396                        if (this.fieldName != null) {
397                                this.field = this.declaringClass.getDeclaredField(this.fieldName);
398                        }
399                        else {
400                                if (this.methodName != null) {
401                                        this.methodParameter = new MethodParameter(
402                                                        this.declaringClass.getDeclaredMethod(this.methodName, this.parameterTypes), this.parameterIndex);
403                                }
404                                else {
405                                        this.methodParameter = new MethodParameter(
406                                                        this.declaringClass.getDeclaredConstructor(this.parameterTypes), this.parameterIndex);
407                                }
408                                for (int i = 1; i < this.nestingLevel; i++) {
409                                        this.methodParameter.increaseNestingLevel();
410                                }
411                        }
412                }
413                catch (Throwable ex) {
414                        throw new IllegalStateException("Could not find original class structure", ex);
415                }
416        }
417
418}