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}