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