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