@ -1,5 +1,5 @@
/ *
/ *
* Copyright 2002 - 2022 the original author or authors .
* Copyright 2002 - 2023 the original author or authors .
*
*
* Licensed under the Apache License , Version 2 . 0 ( the "License" ) ;
* Licensed under the Apache License , Version 2 . 0 ( the "License" ) ;
* you may not use this file except in compliance with the License .
* you may not use this file except in compliance with the License .
@ -45,6 +45,7 @@ import org.springframework.beans.TypeConverter;
import org.springframework.beans.TypeMismatchException ;
import org.springframework.beans.TypeMismatchException ;
import org.springframework.beans.factory.BeanCreationException ;
import org.springframework.beans.factory.BeanCreationException ;
import org.springframework.beans.factory.BeanDefinitionStoreException ;
import org.springframework.beans.factory.BeanDefinitionStoreException ;
import org.springframework.beans.factory.BeanFactory ;
import org.springframework.beans.factory.InjectionPoint ;
import org.springframework.beans.factory.InjectionPoint ;
import org.springframework.beans.factory.NoSuchBeanDefinitionException ;
import org.springframework.beans.factory.NoSuchBeanDefinitionException ;
import org.springframework.beans.factory.NoUniqueBeanDefinitionException ;
import org.springframework.beans.factory.NoUniqueBeanDefinitionException ;
@ -85,12 +86,6 @@ class ConstructorResolver {
private static final Object [ ] EMPTY_ARGS = new Object [ 0 ] ;
private static final Object [ ] EMPTY_ARGS = new Object [ 0 ] ;
/ * *
* Marker for autowired arguments in a cached argument array , to be replaced
* by a { @linkplain # resolveAutowiredArgument resolved autowired argument } .
* /
private static final Object autowiredArgumentMarker = new Object ( ) ;
private static final NamedThreadLocal < InjectionPoint > currentInjectionPoint =
private static final NamedThreadLocal < InjectionPoint > currentInjectionPoint =
new NamedThreadLocal < > ( "Current injection point" ) ;
new NamedThreadLocal < > ( "Current injection point" ) ;
@ -729,7 +724,7 @@ class ConstructorResolver {
ArgumentsHolder args = new ArgumentsHolder ( paramTypes . length ) ;
ArgumentsHolder args = new ArgumentsHolder ( paramTypes . length ) ;
Set < ConstructorArgumentValues . ValueHolder > usedValueHolders = new HashSet < > ( paramTypes . length ) ;
Set < ConstructorArgumentValues . ValueHolder > usedValueHolders = new HashSet < > ( paramTypes . length ) ;
Set < String > autowiredBeanNames = new LinkedHashSet < > ( 4 ) ;
Set < String > allA utowiredBeanNames = new LinkedHashSet < > ( paramTypes . length * 2 ) ;
for ( int paramIndex = 0 ; paramIndex < paramTypes . length ; paramIndex + + ) {
for ( int paramIndex = 0 ; paramIndex < paramTypes . length ; paramIndex + + ) {
Class < ? > paramType = paramTypes [ paramIndex ] ;
Class < ? > paramType = paramTypes [ paramIndex ] ;
@ -764,8 +759,8 @@ class ConstructorResolver {
throw new UnsatisfiedDependencyException (
throw new UnsatisfiedDependencyException (
mbd . getResourceDescription ( ) , beanName , new InjectionPoint ( methodParam ) ,
mbd . getResourceDescription ( ) , beanName , new InjectionPoint ( methodParam ) ,
"Could not convert argument value of type [" +
"Could not convert argument value of type [" +
ObjectUtils . nullSafeClassName ( valueHolder . getValue ( ) ) +
ObjectUtils . nullSafeClassName ( valueHolder . getValue ( ) ) +
"] to required type [" + paramType . getName ( ) + "]: " + ex . getMessage ( ) ) ;
"] to required type [" + paramType . getName ( ) + "]: " + ex . getMessage ( ) ) ;
}
}
Object sourceHolder = valueHolder . getSource ( ) ;
Object sourceHolder = valueHolder . getSource ( ) ;
if ( sourceHolder instanceof ConstructorArgumentValues . ValueHolder ) {
if ( sourceHolder instanceof ConstructorArgumentValues . ValueHolder ) {
@ -788,11 +783,17 @@ class ConstructorResolver {
"] - did you specify the correct bean references as arguments?" ) ;
"] - did you specify the correct bean references as arguments?" ) ;
}
}
try {
try {
Object autowiredArgument = resolveAutowiredArgument (
ConstructorDependencyDescriptor desc = new ConstructorDependencyDescriptor ( methodParam , true ) ;
methodParam , beanName , autowiredBeanNames , converter , fallback ) ;
Set < String > autowiredBeanNames = new LinkedHashSet < > ( 2 ) ;
args . rawArguments [ paramIndex ] = autowiredArgument ;
Object arg = resolveAutowiredArgument (
args . arguments [ paramIndex ] = autowiredArgument ;
desc , paramType , beanName , autowiredBeanNames , converter , fallback ) ;
args . preparedArguments [ paramIndex ] = autowiredArgumentMarker ;
if ( arg ! = null ) {
setShortcutIfPossible ( desc , paramType , autowiredBeanNames ) ;
}
allAutowiredBeanNames . addAll ( autowiredBeanNames ) ;
args . rawArguments [ paramIndex ] = arg ;
args . arguments [ paramIndex ] = arg ;
args . preparedArguments [ paramIndex ] = desc ;
args . resolveNecessary = true ;
args . resolveNecessary = true ;
}
}
catch ( BeansException ex ) {
catch ( BeansException ex ) {
@ -802,14 +803,7 @@ class ConstructorResolver {
}
}
}
}
for ( String autowiredBeanName : autowiredBeanNames ) {
registerDependentBeans ( executable , beanName , allAutowiredBeanNames ) ;
this . beanFactory . registerDependentBean ( autowiredBeanName , beanName ) ;
if ( logger . isDebugEnabled ( ) ) {
logger . debug ( "Autowiring by type from bean name '" + beanName +
"' via " + ( executable instanceof Constructor ? "constructor" : "factory method" ) +
" to bean named '" + autowiredBeanName + "'" ) ;
}
}
return args ;
return args ;
}
}
@ -829,31 +823,57 @@ class ConstructorResolver {
Object [ ] resolvedArgs = new Object [ argsToResolve . length ] ;
Object [ ] resolvedArgs = new Object [ argsToResolve . length ] ;
for ( int argIndex = 0 ; argIndex < argsToResolve . length ; argIndex + + ) {
for ( int argIndex = 0 ; argIndex < argsToResolve . length ; argIndex + + ) {
Object argValue = argsToResolve [ argIndex ] ;
Object argValue = argsToResolve [ argIndex ] ;
MethodParameter methodParam = MethodParameter . forExecutable ( executable , argIndex ) ;
Class < ? > paramType = paramTypes [ argIndex ] ;
if ( argValue = = autowiredArgumentMarker ) {
boolean convertNecessary = false ;
argValue = resolveAutowiredArgument ( methodParam , beanName , null , converter , true ) ;
if ( argValue instanceof ConstructorDependencyDescriptor ) {
ConstructorDependencyDescriptor descriptor = ( ConstructorDependencyDescriptor ) argValue ;
try {
argValue = resolveAutowiredArgument ( descriptor , paramType , beanName ,
null , converter , true ) ;
}
catch ( BeansException ex ) {
// Unexpected target bean mismatch for cached argument -> re-resolve
synchronized ( descriptor ) {
if ( ! descriptor . hasShortcut ( ) ) {
throw ex ;
}
descriptor . setShortcut ( null ) ;
Set < String > autowiredBeanNames = new LinkedHashSet < > ( 2 ) ;
argValue = resolveAutowiredArgument ( descriptor , paramType , beanName ,
autowiredBeanNames , converter , true ) ;
if ( argValue ! = null ) {
setShortcutIfPossible ( descriptor , paramType , autowiredBeanNames ) ;
}
registerDependentBeans ( executable , beanName , autowiredBeanNames ) ;
}
}
}
}
else if ( argValue instanceof BeanMetadataElement ) {
else if ( argValue instanceof BeanMetadataElement ) {
argValue = valueResolver . resolveValueIfNecessary ( "constructor argument" , argValue ) ;
argValue = valueResolver . resolveValueIfNecessary ( "constructor argument" , argValue ) ;
convertNecessary = true ;
}
}
else if ( argValue instanceof String ) {
else if ( argValue instanceof String ) {
argValue = this . beanFactory . evaluateBeanDefinitionString ( ( String ) argValue , mbd ) ;
argValue = this . beanFactory . evaluateBeanDefinitionString ( ( String ) argValue , mbd ) ;
convertNecessary = true ;
}
}
Class < ? > paramType = paramTypes [ argIndex ] ;
if ( convertNecessary ) {
try {
MethodParameter methodParam = MethodParameter . forExecutable ( executable , argIndex ) ;
resolvedArgs [ argIndex ] = converter . convertIfNecessary ( argValue , paramType , methodParam ) ;
try {
}
argValue = converter . convertIfNecessary ( argValue , paramType , methodParam ) ;
catch ( TypeMismatchException ex ) {
}
throw new UnsatisfiedDependencyException (
catch ( TypeMismatchException ex ) {
mbd . getResourceDescription ( ) , beanName , new InjectionPoint ( methodParam ) ,
throw new UnsatisfiedDependencyException (
"Could not convert argument value of type [" + ObjectUtils . nullSafeClassName ( argValue ) +
mbd . getResourceDescription ( ) , beanName , new InjectionPoint ( methodParam ) ,
"] to required type [" + paramType . getName ( ) + "]: " + ex . getMessage ( ) ) ;
"Could not convert argument value of type [" + ObjectUtils . nullSafeClassName ( argValue ) +
"] to required type [" + paramType . getName ( ) + "]: " + ex . getMessage ( ) ) ;
}
}
}
resolvedArgs [ argIndex ] = argValue ;
}
}
return resolvedArgs ;
return resolvedArgs ;
}
}
protected Constructor < ? > getUserDeclaredConstructor ( Constructor < ? > constructor ) {
private Constructor < ? > getUserDeclaredConstructor ( Constructor < ? > constructor ) {
Class < ? > declaringClass = constructor . getDeclaringClass ( ) ;
Class < ? > declaringClass = constructor . getDeclaringClass ( ) ;
Class < ? > userClass = ClassUtils . getUserClass ( declaringClass ) ;
Class < ? > userClass = ClassUtils . getUserClass ( declaringClass ) ;
if ( userClass ! = declaringClass ) {
if ( userClass ! = declaringClass ) {
@ -869,23 +889,22 @@ class ConstructorResolver {
}
}
/ * *
/ * *
* Template method for resolving the specified argument which is supposed to be autowired .
* Resolve the specified argument which is supposed to be autowired .
* /
* /
@Nullable
@Nullable
protected Object resolveAutowiredArgument ( MethodParameter param , String beanName ,
Object resolveAutowiredArgument ( DependencyDescriptor descriptor , Class < ? > paramType , String beanName ,
@Nullable Set < String > autowiredBeanNames , TypeConverter typeConverter , boolean fallback ) {
@Nullable Set < String > autowiredBeanNames , TypeConverter typeConverter , boolean fallback ) {
Class < ? > paramType = param . getParameterType ( ) ;
if ( InjectionPoint . class . isAssignableFrom ( paramType ) ) {
if ( InjectionPoint . class . isAssignableFrom ( paramType ) ) {
InjectionPoint injectionPoint = currentInjectionPoint . get ( ) ;
InjectionPoint injectionPoint = currentInjectionPoint . get ( ) ;
if ( injectionPoint = = null ) {
if ( injectionPoint = = null ) {
throw new IllegalStateException ( "No current InjectionPoint available for " + param ) ;
throw new IllegalStateException ( "No current InjectionPoint available for " + descriptor ) ;
}
}
return injectionPoint ;
return injectionPoint ;
}
}
try {
try {
return this . beanFactory . resolveDependency (
return this . beanFactory . resolveDependency ( descriptor , beanName , autowiredBeanNames , typeConverter ) ;
new DependencyDescriptor ( param , true ) , beanName , autowiredBeanNames , typeConverter ) ;
}
}
catch ( NoUniqueBeanDefinitionException ex ) {
catch ( NoUniqueBeanDefinitionException ex ) {
throw ex ;
throw ex ;
@ -908,6 +927,31 @@ class ConstructorResolver {
}
}
}
}
private void setShortcutIfPossible (
ConstructorDependencyDescriptor descriptor , Class < ? > paramType , Set < String > autowiredBeanNames ) {
if ( autowiredBeanNames . size ( ) = = 1 ) {
String autowiredBeanName = autowiredBeanNames . iterator ( ) . next ( ) ;
if ( this . beanFactory . containsBean ( autowiredBeanName ) & &
this . beanFactory . isTypeMatch ( autowiredBeanName , paramType ) ) {
descriptor . setShortcut ( autowiredBeanName ) ;
}
}
}
private void registerDependentBeans (
Executable executable , String beanName , Set < String > autowiredBeanNames ) {
for ( String autowiredBeanName : autowiredBeanNames ) {
this . beanFactory . registerDependentBean ( autowiredBeanName , beanName ) ;
if ( logger . isDebugEnabled ( ) ) {
logger . debug ( "Autowiring by type from bean name '" + beanName + "' via " +
( executable instanceof Constructor ? "constructor" : "factory method" ) +
" to bean named '" + autowiredBeanName + "'" ) ;
}
}
}
static InjectionPoint setCurrentInjectionPoint ( @Nullable InjectionPoint injectionPoint ) {
static InjectionPoint setCurrentInjectionPoint ( @Nullable InjectionPoint injectionPoint ) {
InjectionPoint old = currentInjectionPoint . get ( ) ;
InjectionPoint old = currentInjectionPoint . get ( ) ;
if ( injectionPoint ! = null ) {
if ( injectionPoint ! = null ) {
@ -1006,4 +1050,35 @@ class ConstructorResolver {
}
}
}
}
/ * *
* DependencyDescriptor marker for constructor arguments ,
* for differentiating between a provided DependencyDescriptor instance
* and an internally built DependencyDescriptor for autowiring purposes .
* /
@SuppressWarnings ( "serial" )
private static class ConstructorDependencyDescriptor extends DependencyDescriptor {
@Nullable
private volatile String shortcut ;
public ConstructorDependencyDescriptor ( MethodParameter methodParameter , boolean required ) {
super ( methodParameter , required ) ;
}
public void setShortcut ( @Nullable String shortcut ) {
this . shortcut = shortcut ;
}
public boolean hasShortcut ( ) {
return ( this . shortcut ! = null ) ;
}
@Override
public Object resolveShortcut ( BeanFactory beanFactory ) {
String shortcut = this . shortcut ;
return ( shortcut ! = null ? beanFactory . getBean ( shortcut , getDependencyType ( ) ) : null ) ;
}
}
}
}