Skip to content

Commit 0d2bfc9

Browse files
committed
Apply consistent ordering in hierarchical contexts
Previously, if `@Order` is specified on a `@Bean` method, and the candidate bean is defined in a parent context, its order wasn't taken into account when retrieving the bean from a child context. This commit makes sure the metadata of a bean is taken into consideration in all cases. Closes gh-29105
1 parent 4e97776 commit 0d2bfc9

File tree

3 files changed

+111
-10
lines changed

3 files changed

+111
-10
lines changed

spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2167,20 +2167,25 @@ public FactoryAwareOrderSourceProvider(Map<Object, String> instancesToBeanNames)
21672167
@Nullable
21682168
public Object getOrderSource(Object obj) {
21692169
String beanName = this.instancesToBeanNames.get(obj);
2170-
if (beanName == null || !containsBeanDefinition(beanName)) {
2170+
if (beanName == null) {
21712171
return null;
21722172
}
2173-
RootBeanDefinition beanDefinition = getMergedLocalBeanDefinition(beanName);
2174-
List<Object> sources = new ArrayList<>(2);
2175-
Method factoryMethod = beanDefinition.getResolvedFactoryMethod();
2176-
if (factoryMethod != null) {
2177-
sources.add(factoryMethod);
2173+
try {
2174+
RootBeanDefinition beanDefinition = (RootBeanDefinition) getMergedBeanDefinition(beanName);
2175+
List<Object> sources = new ArrayList<>(2);
2176+
Method factoryMethod = beanDefinition.getResolvedFactoryMethod();
2177+
if (factoryMethod != null) {
2178+
sources.add(factoryMethod);
2179+
}
2180+
Class<?> targetType = beanDefinition.getTargetType();
2181+
if (targetType != null && targetType != obj.getClass()) {
2182+
sources.add(targetType);
2183+
}
2184+
return sources.toArray();
21782185
}
2179-
Class<?> targetType = beanDefinition.getTargetType();
2180-
if (targetType != null && targetType != obj.getClass()) {
2181-
sources.add(targetType);
2186+
catch (NoSuchBeanDefinitionException ex) {
2187+
return null;
21822188
}
2183-
return sources.toArray();
21842189
}
21852190
}
21862191

spring-beans/src/test/java/org/springframework/beans/factory/DefaultListableBeanFactoryTests.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1982,6 +1982,21 @@ void autowireBeanByTypePrimaryTakesPrecedenceOverPriority() {
19821982
assertThat(bean.getSpouse()).isEqualTo(lbf.getBean("spouse"));
19831983
}
19841984

1985+
@Test
1986+
void beanProviderWithParentBeanFactoryReuseOrder() {
1987+
DefaultListableBeanFactory parentBf = new DefaultListableBeanFactory();
1988+
parentBf.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);
1989+
parentBf.registerBeanDefinition("regular", new RootBeanDefinition(TestBean.class));
1990+
parentBf.registerBeanDefinition("test", new RootBeanDefinition(HighPriorityTestBean.class));
1991+
lbf.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);
1992+
lbf.setParentBeanFactory(parentBf);
1993+
lbf.registerBeanDefinition("low", new RootBeanDefinition(LowPriorityTestBean.class));
1994+
List<Class<?>> orderedTypes = lbf.getBeanProvider(TestBean.class).orderedStream()
1995+
.map(Object::getClass).collect(Collectors.toList());
1996+
assertThat(orderedTypes).containsExactly(
1997+
HighPriorityTestBean.class, LowPriorityTestBean.class, TestBean.class);
1998+
}
1999+
19852000
@Test
19862001
void autowireExistingBeanByName() {
19872002
RootBeanDefinition bd = new RootBeanDefinition(TestBean.class);
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
/*
2+
* Copyright 2002-2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.context.annotation;
18+
19+
import java.util.List;
20+
import java.util.stream.Collectors;
21+
22+
import org.junit.jupiter.api.Test;
23+
24+
import org.springframework.core.annotation.Order;
25+
26+
import static org.assertj.core.api.Assertions.assertThat;
27+
28+
/**
29+
* Tests for gh-29105.
30+
*
31+
* @author Stephane Nicoll
32+
*/
33+
public class Gh29105Tests {
34+
35+
@Test
36+
void beanProviderWithParentContextReuseOrder() {
37+
AnnotationConfigApplicationContext parent = new AnnotationConfigApplicationContext();
38+
parent.register(DefaultConfiguration.class);
39+
parent.register(CustomConfiguration.class);
40+
parent.refresh();
41+
42+
AnnotationConfigApplicationContext child = new AnnotationConfigApplicationContext();
43+
child.setParent(parent);
44+
child.register(DefaultConfiguration.class);
45+
child.refresh();
46+
47+
List<Class<?>> orderedTypes = child.getBeanProvider(MyService.class)
48+
.orderedStream().map(Object::getClass).collect(Collectors.toList());
49+
assertThat(orderedTypes).containsExactly(CustomService.class, DefaultService.class);
50+
}
51+
52+
53+
interface MyService {}
54+
55+
static class CustomService implements MyService {}
56+
57+
static class DefaultService implements MyService {}
58+
59+
60+
@Configuration
61+
static class CustomConfiguration {
62+
63+
@Bean
64+
@Order(-1)
65+
CustomService customService() {
66+
return new CustomService();
67+
}
68+
69+
}
70+
71+
@Configuration
72+
static class DefaultConfiguration {
73+
74+
@Bean
75+
@Order(0)
76+
DefaultService defaultService() {
77+
return new DefaultService();
78+
}
79+
80+
}
81+
}

0 commit comments

Comments
 (0)