Skip to content

Commit a2f65cd

Browse files
wilkinsonaphilwebb
andcommitted
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 <[email protected]>
1 parent d6ecdd5 commit a2f65cd

File tree

2 files changed

+36
-11
lines changed

2 files changed

+36
-11
lines changed

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/DefaultBindConstructorProvider.java

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,15 @@
1919
import java.lang.reflect.Constructor;
2020
import java.lang.reflect.Modifier;
2121
import java.util.Arrays;
22+
import java.util.stream.Stream;
2223

2324
import org.springframework.beans.BeanUtils;
2425
import org.springframework.beans.factory.annotation.Autowired;
2526
import org.springframework.core.KotlinDetector;
2627
import org.springframework.core.annotation.MergedAnnotations;
28+
import org.springframework.core.annotation.MergedAnnotations.SearchStrategy;
2729
import org.springframework.util.Assert;
30+
import org.springframework.util.ClassUtils;
2831

2932
/**
3033
* Default {@link BindConstructorProvider} implementation.
@@ -75,9 +78,9 @@ Constructor<?> getBind() {
7578
}
7679

7780
static Constructors getConstructors(Class<?> type) {
81+
boolean hasAutowiredConstructor = isAutowiredPresent(type);
7882
Constructor<?>[] candidates = getCandidateConstructors(type);
7983
MergedAnnotations[] candidateAnnotations = getAnnotations(candidates);
80-
boolean hasAutowiredConstructor = isAutowiredPresent(candidateAnnotations);
8184
Constructor<?> bind = getConstructorBindingAnnotated(type, candidates, candidateAnnotations);
8285
if (bind == null && !hasAutowiredConstructor) {
8386
bind = deduceBindConstructor(type, candidates);
@@ -88,6 +91,15 @@ static Constructors getConstructors(Class<?> type) {
8891
return new Constructors(hasAutowiredConstructor, bind);
8992
}
9093

94+
private static boolean isAutowiredPresent(Class<?> type) {
95+
if (Stream.of(type.getDeclaredConstructors()).map(MergedAnnotations::from)
96+
.anyMatch((annotations) -> annotations.isPresent(Autowired.class))) {
97+
return true;
98+
}
99+
Class<?> userClass = ClassUtils.getUserClass(type);
100+
return (userClass != type) ? isAutowiredPresent(userClass) : false;
101+
}
102+
91103
private static Constructor<?>[] getCandidateConstructors(Class<?> type) {
92104
if (isInnerClass(type)) {
93105
return new Constructor<?>[0];
@@ -112,20 +124,11 @@ private static boolean isNonSynthetic(Constructor<?> constructor, Class<?> type)
112124
private static MergedAnnotations[] getAnnotations(Constructor<?>[] candidates) {
113125
MergedAnnotations[] candidateAnnotations = new MergedAnnotations[candidates.length];
114126
for (int i = 0; i < candidates.length; i++) {
115-
candidateAnnotations[i] = MergedAnnotations.from(candidates[i]);
127+
candidateAnnotations[i] = MergedAnnotations.from(candidates[i], SearchStrategy.SUPERCLASS);
116128
}
117129
return candidateAnnotations;
118130
}
119131

120-
private static boolean isAutowiredPresent(MergedAnnotations[] candidateAnnotations) {
121-
for (MergedAnnotations annotations : candidateAnnotations) {
122-
if (annotations.isPresent(Autowired.class)) {
123-
return true;
124-
}
125-
}
126-
return false;
127-
}
128-
129132
private static Constructor<?> getConstructorBindingAnnotated(Class<?> type, Constructor<?>[] candidates,
130133
MergedAnnotations[] mergedAnnotations) {
131134
Constructor<?> result = null;

spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/bind/DefaultBindConstructorProviderTests.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@
2121
import org.junit.jupiter.api.Test;
2222

2323
import org.springframework.beans.factory.annotation.Autowired;
24+
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
25+
import org.springframework.context.annotation.Configuration;
26+
import org.springframework.core.env.Environment;
2427

2528
import static org.assertj.core.api.Assertions.assertThat;
2629
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
@@ -97,6 +100,16 @@ void getBindConstructorWhenIsMemberTypeWithPrivateConstructorReturnsNull() {
97100
assertThat(constructor).isNull();
98101
}
99102

103+
@Test
104+
void getBindConstructorFromProxiedClassWithOneAutowiredConstructorReturnsNull() {
105+
try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
106+
ProxiedWithOneConstructorWithAutowired.class)) {
107+
ProxiedWithOneConstructorWithAutowired bean = context.getBean(ProxiedWithOneConstructorWithAutowired.class);
108+
Constructor<?> bindConstructor = this.provider.getBindConstructor(bean.getClass(), false);
109+
assertThat(bindConstructor).isNull();
110+
}
111+
}
112+
100113
static class OnlyDefaultConstructor {
101114

102115
}
@@ -188,4 +201,13 @@ private Member(String name) {
188201

189202
}
190203

204+
@Configuration
205+
static class ProxiedWithOneConstructorWithAutowired {
206+
207+
@Autowired
208+
ProxiedWithOneConstructorWithAutowired(Environment environment) {
209+
}
210+
211+
}
212+
191213
}

0 commit comments

Comments
 (0)