Skip to content

Commit

Permalink
Consider user classes when finding bind constructor
Browse files Browse the repository at this point in the history
Update `@Autowired` detection logic to consider all constructors and to
search user classes.

Fixes gh-33061

Co-authored-by: Phillip Webb <pwebb@vmware.com>
  • Loading branch information
wilkinsona and philwebb committed Nov 10, 2022
1 parent d6ecdd5 commit a2f65cd
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,15 @@
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.
Expand Down Expand Up @@ -75,9 +78,9 @@ Constructor<?> getBind() {
}

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);
Expand All @@ -88,6 +91,15 @@ static Constructors getConstructors(Class<?> type) {
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];
Expand All @@ -112,20 +124,11 @@ private static boolean isNonSynthetic(Constructor<?> constructor, Class<?> type)
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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@
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;
Expand Down Expand Up @@ -97,6 +100,16 @@ void getBindConstructorWhenIsMemberTypeWithPrivateConstructorReturnsNull() {
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 {

}
Expand Down Expand Up @@ -188,4 +201,13 @@ private Member(String name) {

}

@Configuration
static class ProxiedWithOneConstructorWithAutowired {

@Autowired
ProxiedWithOneConstructorWithAutowired(Environment environment) {
}

}

}

0 comments on commit a2f65cd

Please sign in to comment.