Consider user classes when finding bind constructor
Update `@Autowired` detection logic to consider all constructors and to search user classes. Fixes gh-33061 Co-authored-by: Phillip Webb <pwebb@vmware.com>
This commit is contained in:
		
							parent
							
								
									d6ecdd5741
								
							
						
					
					
						commit
						a2f65cd887
					
				|  | @ -19,12 +19,15 @@ package org.springframework.boot.context.properties.bind; | |||
| import java.lang.reflect.Constructor; | ||||
| import java.lang.reflect.Modifier; | ||||
| import java.util.Arrays; | ||||
| import java.util.stream.Stream; | ||||
| 
 | ||||
| import org.springframework.beans.BeanUtils; | ||||
| import org.springframework.beans.factory.annotation.Autowired; | ||||
| import org.springframework.core.KotlinDetector; | ||||
| import org.springframework.core.annotation.MergedAnnotations; | ||||
| import org.springframework.core.annotation.MergedAnnotations.SearchStrategy; | ||||
| import org.springframework.util.Assert; | ||||
| import org.springframework.util.ClassUtils; | ||||
| 
 | ||||
| /** | ||||
|  * Default {@link BindConstructorProvider} implementation. | ||||
|  | @ -75,9 +78,9 @@ class DefaultBindConstructorProvider implements BindConstructorProvider { | |||
| 		} | ||||
| 
 | ||||
| 		static Constructors getConstructors(Class<?> type) { | ||||
| 			boolean hasAutowiredConstructor = isAutowiredPresent(type); | ||||
| 			Constructor<?>[] candidates = getCandidateConstructors(type); | ||||
| 			MergedAnnotations[] candidateAnnotations = getAnnotations(candidates); | ||||
| 			boolean hasAutowiredConstructor = isAutowiredPresent(candidateAnnotations); | ||||
| 			Constructor<?> bind = getConstructorBindingAnnotated(type, candidates, candidateAnnotations); | ||||
| 			if (bind == null && !hasAutowiredConstructor) { | ||||
| 				bind = deduceBindConstructor(type, candidates); | ||||
|  | @ -88,6 +91,15 @@ class DefaultBindConstructorProvider implements BindConstructorProvider { | |||
| 			return new Constructors(hasAutowiredConstructor, bind); | ||||
| 		} | ||||
| 
 | ||||
| 		private static boolean isAutowiredPresent(Class<?> type) { | ||||
| 			if (Stream.of(type.getDeclaredConstructors()).map(MergedAnnotations::from) | ||||
| 					.anyMatch((annotations) -> annotations.isPresent(Autowired.class))) { | ||||
| 				return true; | ||||
| 			} | ||||
| 			Class<?> userClass = ClassUtils.getUserClass(type); | ||||
| 			return (userClass != type) ? isAutowiredPresent(userClass) : false; | ||||
| 		} | ||||
| 
 | ||||
| 		private static Constructor<?>[] getCandidateConstructors(Class<?> type) { | ||||
| 			if (isInnerClass(type)) { | ||||
| 				return new Constructor<?>[0]; | ||||
|  | @ -112,20 +124,11 @@ class DefaultBindConstructorProvider implements BindConstructorProvider { | |||
| 		private static MergedAnnotations[] getAnnotations(Constructor<?>[] candidates) { | ||||
| 			MergedAnnotations[] candidateAnnotations = new MergedAnnotations[candidates.length]; | ||||
| 			for (int i = 0; i < candidates.length; i++) { | ||||
| 				candidateAnnotations[i] = MergedAnnotations.from(candidates[i]); | ||||
| 				candidateAnnotations[i] = MergedAnnotations.from(candidates[i], SearchStrategy.SUPERCLASS); | ||||
| 			} | ||||
| 			return candidateAnnotations; | ||||
| 		} | ||||
| 
 | ||||
| 		private static boolean isAutowiredPresent(MergedAnnotations[] candidateAnnotations) { | ||||
| 			for (MergedAnnotations annotations : candidateAnnotations) { | ||||
| 				if (annotations.isPresent(Autowired.class)) { | ||||
| 					return true; | ||||
| 				} | ||||
| 			} | ||||
| 			return false; | ||||
| 		} | ||||
| 
 | ||||
| 		private static Constructor<?> getConstructorBindingAnnotated(Class<?> type, Constructor<?>[] candidates, | ||||
| 				MergedAnnotations[] mergedAnnotations) { | ||||
| 			Constructor<?> result = null; | ||||
|  |  | |||
|  | @ -21,6 +21,9 @@ import java.lang.reflect.Constructor; | |||
| import org.junit.jupiter.api.Test; | ||||
| 
 | ||||
| import org.springframework.beans.factory.annotation.Autowired; | ||||
| import org.springframework.context.annotation.AnnotationConfigApplicationContext; | ||||
| import org.springframework.context.annotation.Configuration; | ||||
| import org.springframework.core.env.Environment; | ||||
| 
 | ||||
| import static org.assertj.core.api.Assertions.assertThat; | ||||
| import static org.assertj.core.api.Assertions.assertThatIllegalStateException; | ||||
|  | @ -97,6 +100,16 @@ class DefaultBindConstructorProviderTests { | |||
| 		assertThat(constructor).isNull(); | ||||
| 	} | ||||
| 
 | ||||
| 	@Test | ||||
| 	void getBindConstructorFromProxiedClassWithOneAutowiredConstructorReturnsNull() { | ||||
| 		try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext( | ||||
| 				ProxiedWithOneConstructorWithAutowired.class)) { | ||||
| 			ProxiedWithOneConstructorWithAutowired bean = context.getBean(ProxiedWithOneConstructorWithAutowired.class); | ||||
| 			Constructor<?> bindConstructor = this.provider.getBindConstructor(bean.getClass(), false); | ||||
| 			assertThat(bindConstructor).isNull(); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	static class OnlyDefaultConstructor { | ||||
| 
 | ||||
| 	} | ||||
|  | @ -188,4 +201,13 @@ class DefaultBindConstructorProviderTests { | |||
| 
 | ||||
| 	} | ||||
| 
 | ||||
| 	@Configuration | ||||
| 	static class ProxiedWithOneConstructorWithAutowired { | ||||
| 
 | ||||
| 		@Autowired | ||||
| 		ProxiedWithOneConstructorWithAutowired(Environment environment) { | ||||
| 		} | ||||
| 
 | ||||
| 	} | ||||
| 
 | ||||
| } | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue