Fix annotation matching when using scoped proxies
Update `OnBeanCondition` to check `isAutowireCandidate` on the original bean of scoped proxy targets. Fixes gh-43423
This commit is contained in:
parent
8ca8ab14f6
commit
86b0c768e7
|
|
@ -202,16 +202,10 @@ class OnBeanCondition extends FilteringSpringBootCondition implements Configurat
|
||||||
}
|
}
|
||||||
|
|
||||||
protected final MatchResult getMatchingBeans(Spec<?> spec) {
|
protected final MatchResult getMatchingBeans(Spec<?> spec) {
|
||||||
|
ConfigurableListableBeanFactory beanFactory = getSearchBeanFactory(spec);
|
||||||
ClassLoader classLoader = spec.getContext().getClassLoader();
|
ClassLoader classLoader = spec.getContext().getClassLoader();
|
||||||
ConfigurableListableBeanFactory beanFactory = spec.getContext().getBeanFactory();
|
|
||||||
boolean considerHierarchy = spec.getStrategy() != SearchStrategy.CURRENT;
|
boolean considerHierarchy = spec.getStrategy() != SearchStrategy.CURRENT;
|
||||||
Set<Class<?>> parameterizedContainers = spec.getParameterizedContainers();
|
Set<Class<?>> parameterizedContainers = spec.getParameterizedContainers();
|
||||||
if (spec.getStrategy() == SearchStrategy.ANCESTORS) {
|
|
||||||
BeanFactory parent = beanFactory.getParentBeanFactory();
|
|
||||||
Assert.isInstanceOf(ConfigurableListableBeanFactory.class, parent,
|
|
||||||
"Unable to use SearchStrategy.ANCESTORS");
|
|
||||||
beanFactory = (ConfigurableListableBeanFactory) parent;
|
|
||||||
}
|
|
||||||
MatchResult result = new MatchResult();
|
MatchResult result = new MatchResult();
|
||||||
Set<String> beansIgnoredByType = getNamesOfBeansIgnoredByType(classLoader, beanFactory, considerHierarchy,
|
Set<String> beansIgnoredByType = getNamesOfBeansIgnoredByType(classLoader, beanFactory, considerHierarchy,
|
||||||
spec.getIgnoredTypes(), parameterizedContainers);
|
spec.getIgnoredTypes(), parameterizedContainers);
|
||||||
|
|
@ -219,8 +213,8 @@ class OnBeanCondition extends FilteringSpringBootCondition implements Configurat
|
||||||
Map<String, BeanDefinition> typeMatchedDefinitions = getBeanDefinitionsForType(classLoader,
|
Map<String, BeanDefinition> typeMatchedDefinitions = getBeanDefinitionsForType(classLoader,
|
||||||
considerHierarchy, beanFactory, type, parameterizedContainers);
|
considerHierarchy, beanFactory, type, parameterizedContainers);
|
||||||
Set<String> typeMatchedNames = matchedNamesFrom(typeMatchedDefinitions,
|
Set<String> typeMatchedNames = matchedNamesFrom(typeMatchedDefinitions,
|
||||||
(name, definition) -> isCandidate(name, definition, beansIgnoredByType)
|
(name, definition) -> !ScopedProxyUtils.isScopedTarget(name)
|
||||||
&& !ScopedProxyUtils.isScopedTarget(name));
|
&& isCandidate(beanFactory, name, definition, beansIgnoredByType));
|
||||||
if (typeMatchedNames.isEmpty()) {
|
if (typeMatchedNames.isEmpty()) {
|
||||||
result.recordUnmatchedType(type);
|
result.recordUnmatchedType(type);
|
||||||
}
|
}
|
||||||
|
|
@ -232,7 +226,7 @@ class OnBeanCondition extends FilteringSpringBootCondition implements Configurat
|
||||||
Map<String, BeanDefinition> annotationMatchedDefinitions = getBeanDefinitionsForAnnotation(classLoader,
|
Map<String, BeanDefinition> annotationMatchedDefinitions = getBeanDefinitionsForAnnotation(classLoader,
|
||||||
beanFactory, annotation, considerHierarchy);
|
beanFactory, annotation, considerHierarchy);
|
||||||
Set<String> annotationMatchedNames = matchedNamesFrom(annotationMatchedDefinitions,
|
Set<String> annotationMatchedNames = matchedNamesFrom(annotationMatchedDefinitions,
|
||||||
(name, definition) -> isCandidate(name, definition, beansIgnoredByType));
|
(name, definition) -> isCandidate(beanFactory, name, definition, beansIgnoredByType));
|
||||||
if (annotationMatchedNames.isEmpty()) {
|
if (annotationMatchedNames.isEmpty()) {
|
||||||
result.recordUnmatchedAnnotation(annotation);
|
result.recordUnmatchedAnnotation(annotation);
|
||||||
}
|
}
|
||||||
|
|
@ -252,6 +246,17 @@ class OnBeanCondition extends FilteringSpringBootCondition implements Configurat
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ConfigurableListableBeanFactory getSearchBeanFactory(Spec<?> spec) {
|
||||||
|
ConfigurableListableBeanFactory beanFactory = spec.getContext().getBeanFactory();
|
||||||
|
if (spec.getStrategy() == SearchStrategy.ANCESTORS) {
|
||||||
|
BeanFactory parent = beanFactory.getParentBeanFactory();
|
||||||
|
Assert.isInstanceOf(ConfigurableListableBeanFactory.class, parent,
|
||||||
|
"Unable to use SearchStrategy.ANCESTORS");
|
||||||
|
beanFactory = (ConfigurableListableBeanFactory) parent;
|
||||||
|
}
|
||||||
|
return beanFactory;
|
||||||
|
}
|
||||||
|
|
||||||
private Set<String> matchedNamesFrom(Map<String, BeanDefinition> namedDefinitions,
|
private Set<String> matchedNamesFrom(Map<String, BeanDefinition> namedDefinitions,
|
||||||
BiPredicate<String, BeanDefinition> filter) {
|
BiPredicate<String, BeanDefinition> filter) {
|
||||||
Set<String> matchedNames = new LinkedHashSet<>(namedDefinitions.size());
|
Set<String> matchedNames = new LinkedHashSet<>(namedDefinitions.size());
|
||||||
|
|
@ -263,9 +268,25 @@ class OnBeanCondition extends FilteringSpringBootCondition implements Configurat
|
||||||
return matchedNames;
|
return matchedNames;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isCandidate(String name, BeanDefinition definition, Set<String> ignoredBeans) {
|
private boolean isCandidate(ConfigurableListableBeanFactory beanFactory, String name, BeanDefinition definition,
|
||||||
return (!ignoredBeans.contains(name))
|
Set<String> ignoredBeans) {
|
||||||
&& (definition == null || (definition.isAutowireCandidate() && isDefaultCandidate(definition)));
|
return (!ignoredBeans.contains(name)) && (definition == null
|
||||||
|
|| isAutowireCandidate(beanFactory, name, definition) && isDefaultCandidate(definition));
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isAutowireCandidate(ConfigurableListableBeanFactory beanFactory, String name,
|
||||||
|
BeanDefinition definition) {
|
||||||
|
return definition.isAutowireCandidate() || isScopeTargetAutowireCandidate(beanFactory, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isScopeTargetAutowireCandidate(ConfigurableListableBeanFactory beanFactory, String name) {
|
||||||
|
try {
|
||||||
|
return ScopedProxyUtils.isScopedTarget(name)
|
||||||
|
&& beanFactory.getBeanDefinition(ScopedProxyUtils.getOriginalBeanName(name)).isAutowireCandidate();
|
||||||
|
}
|
||||||
|
catch (NoSuchBeanDefinitionException ex) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isDefaultCandidate(BeanDefinition definition) {
|
private boolean isDefaultCandidate(BeanDefinition definition) {
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,9 @@ import org.springframework.context.annotation.FilterType;
|
||||||
import org.springframework.context.annotation.Import;
|
import org.springframework.context.annotation.Import;
|
||||||
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
|
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
|
||||||
import org.springframework.context.annotation.ImportResource;
|
import org.springframework.context.annotation.ImportResource;
|
||||||
|
import org.springframework.context.annotation.Scope;
|
||||||
|
import org.springframework.context.annotation.ScopedProxyMode;
|
||||||
|
import org.springframework.context.support.SimpleThreadScope;
|
||||||
import org.springframework.core.type.AnnotationMetadata;
|
import org.springframework.core.type.AnnotationMetadata;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
|
|
@ -124,6 +127,16 @@ class ConditionalOnMissingBeanTests {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testAnnotationOnMissingBeanConditionWithScopedProxy() {
|
||||||
|
this.contextRunner.withInitializer(this::registerScope)
|
||||||
|
.withUserConfiguration(ScopedExampleBeanConfiguration.class, OnAnnotationConfiguration.class)
|
||||||
|
.run((context) -> {
|
||||||
|
assertThat(context).doesNotHaveBean("bar");
|
||||||
|
assertThat(context.getBean(ScopedExampleBean.class)).hasToString("test");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testAnnotationOnMissingBeanConditionWithEagerFactoryBean() {
|
void testAnnotationOnMissingBeanConditionWithEagerFactoryBean() {
|
||||||
// Rigorous test for SPR-11069
|
// Rigorous test for SPR-11069
|
||||||
|
|
@ -407,6 +420,10 @@ class ConditionalOnMissingBeanTests {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void registerScope(ConfigurableApplicationContext applicationContext) {
|
||||||
|
applicationContext.getBeanFactory().registerScope("test", new TestScope());
|
||||||
|
}
|
||||||
|
|
||||||
@Configuration(proxyBeanMethods = false)
|
@Configuration(proxyBeanMethods = false)
|
||||||
static class OnBeanInAncestorsConfiguration {
|
static class OnBeanInAncestorsConfiguration {
|
||||||
|
|
||||||
|
|
@ -665,6 +682,17 @@ class ConditionalOnMissingBeanTests {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Configuration(proxyBeanMethods = false)
|
||||||
|
@TestAnnotation
|
||||||
|
static class ScopedFooConfiguration {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
String foo() {
|
||||||
|
return "foo";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@Configuration(proxyBeanMethods = false)
|
@Configuration(proxyBeanMethods = false)
|
||||||
static class NotAutowireCandidateConfig {
|
static class NotAutowireCandidateConfig {
|
||||||
|
|
||||||
|
|
@ -717,6 +745,12 @@ class ConditionalOnMissingBeanTests {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Configuration(proxyBeanMethods = false)
|
||||||
|
@Import(ScopedExampleBean.class)
|
||||||
|
static class ScopedExampleBeanConfiguration {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@Configuration(proxyBeanMethods = false)
|
@Configuration(proxyBeanMethods = false)
|
||||||
static class UnrelatedExampleBeanConfiguration {
|
static class UnrelatedExampleBeanConfiguration {
|
||||||
|
|
||||||
|
|
@ -893,6 +927,15 @@ class ConditionalOnMissingBeanTests {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Scope(scopeName = "test", proxyMode = ScopedProxyMode.TARGET_CLASS)
|
||||||
|
static class ScopedExampleBean extends ExampleBean {
|
||||||
|
|
||||||
|
ScopedExampleBean() {
|
||||||
|
super("test");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
static class CustomExampleBean extends ExampleBean {
|
static class CustomExampleBean extends ExampleBean {
|
||||||
|
|
||||||
CustomExampleBean() {
|
CustomExampleBean() {
|
||||||
|
|
@ -931,4 +974,8 @@ class ConditionalOnMissingBeanTests {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static class TestScope extends SimpleThreadScope {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue