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;
018
019import java.lang.annotation.Annotation;
020import java.lang.reflect.AnnotatedElement;
021import java.lang.reflect.Field;
022import java.lang.reflect.Member;
023
024import org.springframework.core.MethodParameter;
025import org.springframework.lang.Nullable;
026import org.springframework.util.Assert;
027import org.springframework.util.ObjectUtils;
028
029/**
030 * A simple descriptor for an injection point, pointing to a method/constructor
031 * parameter or a field. Exposed by {@link UnsatisfiedDependencyException}.
032 * Also available as an argument for factory methods, reacting to the
033 * requesting injection point for building a customized bean instance.
034 *
035 * @author Juergen Hoeller
036 * @since 4.3
037 * @see UnsatisfiedDependencyException#getInjectionPoint()
038 * @see org.springframework.beans.factory.config.DependencyDescriptor
039 */
040public class InjectionPoint {
041
042        @Nullable
043        protected MethodParameter methodParameter;
044
045        @Nullable
046        protected Field field;
047
048        @Nullable
049        private volatile Annotation[] fieldAnnotations;
050
051
052        /**
053         * Create an injection point descriptor for a method or constructor parameter.
054         * @param methodParameter the MethodParameter to wrap
055         */
056        public InjectionPoint(MethodParameter methodParameter) {
057                Assert.notNull(methodParameter, "MethodParameter must not be null");
058                this.methodParameter = methodParameter;
059        }
060
061        /**
062         * Create an injection point descriptor for a field.
063         * @param field the field to wrap
064         */
065        public InjectionPoint(Field field) {
066                Assert.notNull(field, "Field must not be null");
067                this.field = field;
068        }
069
070        /**
071         * Copy constructor.
072         * @param original the original descriptor to create a copy from
073         */
074        protected InjectionPoint(InjectionPoint original) {
075                this.methodParameter = (original.methodParameter != null ?
076                                new MethodParameter(original.methodParameter) : null);
077                this.field = original.field;
078                this.fieldAnnotations = original.fieldAnnotations;
079        }
080
081        /**
082         * Just available for serialization purposes in subclasses.
083         */
084        protected InjectionPoint() {
085        }
086
087
088        /**
089         * Return the wrapped MethodParameter, if any.
090         * <p>Note: Either MethodParameter or Field is available.
091         * @return the MethodParameter, or {@code null} if none
092         */
093        @Nullable
094        public MethodParameter getMethodParameter() {
095                return this.methodParameter;
096        }
097
098        /**
099         * Return the wrapped Field, if any.
100         * <p>Note: Either MethodParameter or Field is available.
101         * @return the Field, or {@code null} if none
102         */
103        @Nullable
104        public Field getField() {
105                return this.field;
106        }
107
108        /**
109         * Return the wrapped MethodParameter, assuming it is present.
110         * @return the MethodParameter (never {@code null})
111         * @throws IllegalStateException if no MethodParameter is available
112         * @since 5.0
113         */
114        protected final MethodParameter obtainMethodParameter() {
115                Assert.state(this.methodParameter != null, "Neither Field nor MethodParameter");
116                return this.methodParameter;
117        }
118
119        /**
120         * Obtain the annotations associated with the wrapped field or method/constructor parameter.
121         */
122        public Annotation[] getAnnotations() {
123                if (this.field != null) {
124                        Annotation[] fieldAnnotations = this.fieldAnnotations;
125                        if (fieldAnnotations == null) {
126                                fieldAnnotations = this.field.getAnnotations();
127                                this.fieldAnnotations = fieldAnnotations;
128                        }
129                        return fieldAnnotations;
130                }
131                else {
132                        return obtainMethodParameter().getParameterAnnotations();
133                }
134        }
135
136        /**
137         * Retrieve a field/parameter annotation of the given type, if any.
138         * @param annotationType the annotation type to retrieve
139         * @return the annotation instance, or {@code null} if none found
140         * @since 4.3.9
141         */
142        @Nullable
143        public <A extends Annotation> A getAnnotation(Class<A> annotationType) {
144                return (this.field != null ? this.field.getAnnotation(annotationType) :
145                                obtainMethodParameter().getParameterAnnotation(annotationType));
146        }
147
148        /**
149         * Return the type declared by the underlying field or method/constructor parameter,
150         * indicating the injection type.
151         */
152        public Class<?> getDeclaredType() {
153                return (this.field != null ? this.field.getType() : obtainMethodParameter().getParameterType());
154        }
155
156        /**
157         * Returns the wrapped member, containing the injection point.
158         * @return the Field / Method / Constructor as Member
159         */
160        public Member getMember() {
161                return (this.field != null ? this.field : obtainMethodParameter().getMember());
162        }
163
164        /**
165         * Return the wrapped annotated element.
166         * <p>Note: In case of a method/constructor parameter, this exposes
167         * the annotations declared on the method or constructor itself
168         * (i.e. at the method/constructor level, not at the parameter level).
169         * Use {@link #getAnnotations()} to obtain parameter-level annotations in
170         * such a scenario, transparently with corresponding field annotations.
171         * @return the Field / Method / Constructor as AnnotatedElement
172         */
173        public AnnotatedElement getAnnotatedElement() {
174                return (this.field != null ? this.field : obtainMethodParameter().getAnnotatedElement());
175        }
176
177
178        @Override
179        public boolean equals(@Nullable Object other) {
180                if (this == other) {
181                        return true;
182                }
183                if (other == null || getClass() != other.getClass()) {
184                        return false;
185                }
186                InjectionPoint otherPoint = (InjectionPoint) other;
187                return (ObjectUtils.nullSafeEquals(this.field, otherPoint.field) &&
188                                ObjectUtils.nullSafeEquals(this.methodParameter, otherPoint.methodParameter));
189        }
190
191        @Override
192        public int hashCode() {
193                return (this.field != null ? this.field.hashCode() : ObjectUtils.nullSafeHashCode(this.methodParameter));
194        }
195
196        @Override
197        public String toString() {
198                return (this.field != null ? "field '" + this.field.getName() + "'" : String.valueOf(this.methodParameter));
199        }
200
201}