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}