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