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