diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java index 09c83104f37..316220dfdc6 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java @@ -29,6 +29,7 @@ import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.Collections; import java.util.Comparator; import java.util.IdentityHashMap; import java.util.Iterator; @@ -2315,6 +2316,33 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto return (result instanceof Optional optional ? optional : Optional.ofNullable(result)); } + /** + * Public method to determine the applicable order value for a given bean. + *

This variant implicitly obtains a corresponding bean instance from this factory. + * @param beanName the name of the bean + * @return the corresponding order value (default is {@link Ordered#LOWEST_PRECEDENCE}) + * @since 7.0 + * @see #getOrder(String, Object) + */ + public int getOrder(String beanName) { + return getOrder(beanName, getBean(beanName)); + } + + /** + * Public method to determine the applicable order value for a given bean. + * @param beanName the name of the bean + * @param beanInstance the bean instance to check + * @return the corresponding order value (default is {@link Ordered#LOWEST_PRECEDENCE}) + * @since 7.0 + * @see #getOrder(String) + */ + public int getOrder(String beanName, Object beanInstance) { + OrderComparator comparator = (getDependencyComparator() instanceof OrderComparator orderComparator ? + orderComparator : OrderComparator.INSTANCE); + return comparator.getOrder(beanInstance, + new FactoryAwareOrderSourceProvider(Collections.singletonMap(beanInstance, beanName))); + } + @Override public String toString() { @@ -2672,7 +2700,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto return null; } try { - RootBeanDefinition beanDefinition = (RootBeanDefinition) getMergedBeanDefinition(beanName); + BeanDefinition beanDefinition = getMergedBeanDefinition(beanName); List sources = new ArrayList<>(3); Object orderAttribute = beanDefinition.getAttribute(AbstractBeanDefinition.ORDER_ATTRIBUTE); if (orderAttribute != null) { @@ -2684,13 +2712,15 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto AbstractBeanDefinition.ORDER_ATTRIBUTE + "': " + orderAttribute.getClass().getName()); } } - Method factoryMethod = beanDefinition.getResolvedFactoryMethod(); - if (factoryMethod != null) { - sources.add(factoryMethod); - } - Class targetType = beanDefinition.getTargetType(); - if (targetType != null && targetType != obj.getClass()) { - sources.add(targetType); + if (beanDefinition instanceof RootBeanDefinition rootBeanDefinition) { + Method factoryMethod = rootBeanDefinition.getResolvedFactoryMethod(); + if (factoryMethod != null) { + sources.add(factoryMethod); + } + Class targetType = rootBeanDefinition.getTargetType(); + if (targetType != null && targetType != obj.getClass()) { + sources.add(targetType); + } } return sources.toArray(); } diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/DefaultListableBeanFactoryTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/DefaultListableBeanFactoryTests.java index 4d50da34b33..eb5e0da5746 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/DefaultListableBeanFactoryTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/DefaultListableBeanFactoryTests.java @@ -1542,6 +1542,7 @@ class DefaultListableBeanFactoryTests { bd2.setAttribute(AbstractBeanDefinition.ORDER_ATTRIBUTE, Ordered.HIGHEST_PRECEDENCE); bd2.setScope(BeanDefinition.SCOPE_PROTOTYPE); lbf.registerBeanDefinition("bean2", bd2); + assertThat(lbf.getBeanProvider(TestBean.class).orderedStream().map(TestBean::getName)) .containsExactly("highest", "lowest"); assertThat(lbf.getBeanProvider(TestBean.class).orderedStream(clazz -> !DerivedTestBean.class.isAssignableFrom(clazz)) @@ -1550,6 +1551,9 @@ class DefaultListableBeanFactoryTests { .containsExactly("highest", "lowest"); assertThat(lbf.getBeanProvider(TestBean.class).orderedStream(ObjectProvider.UNFILTERED, false).map(TestBean::getName)) .containsExactly("lowest"); + + assertThat(lbf.getOrder("bean1")).isEqualTo(Ordered.LOWEST_PRECEDENCE); + assertThat(lbf.getOrder("bean2")).isEqualTo(Ordered.HIGHEST_PRECEDENCE); } @Test @@ -1562,12 +1566,16 @@ class DefaultListableBeanFactoryTests { rbd2.setAttribute(AbstractBeanDefinition.ORDER_ATTRIBUTE, Ordered.LOWEST_PRECEDENCE); rbd2.setScope(BeanDefinition.SCOPE_PROTOTYPE); lbf.registerBeanDefinition("highestPrecedenceFactory", rbd2); + assertThat(lbf.getBeanProvider(TestBean.class).orderedStream().map(TestBean::getName)) .containsExactly("fromLowestPrecedenceTestBeanFactoryBean", "fromHighestPrecedenceTestBeanFactoryBean"); assertThat(lbf.getBeanProvider(TestBean.class).orderedStream(ObjectProvider.UNFILTERED).map(TestBean::getName)) .containsExactly("fromLowestPrecedenceTestBeanFactoryBean", "fromHighestPrecedenceTestBeanFactoryBean"); assertThat(lbf.getBeanProvider(TestBean.class).orderedStream(ObjectProvider.UNFILTERED, false).map(TestBean::getName)) .containsExactly("fromLowestPrecedenceTestBeanFactoryBean"); + + assertThat(lbf.getOrder("lowestPrecedenceFactory")).isEqualTo(Ordered.HIGHEST_PRECEDENCE); + assertThat(lbf.getOrder("highestPrecedenceFactory")).isEqualTo(Ordered.LOWEST_PRECEDENCE); } @Test @@ -1579,6 +1587,7 @@ class DefaultListableBeanFactoryTests { GenericBeanDefinition bd2 = new GenericBeanDefinition(); bd2.setBeanClass(TestBean.class); lbf.registerBeanDefinition("bean", bd2); + assertThatIllegalStateException() .isThrownBy(() -> lbf.getBeanProvider(TestBean.class).orderedStream().collect(Collectors.toList())) .withMessageContaining("Invalid value type for attribute"); diff --git a/spring-context/src/test/java/org/springframework/context/annotation/Gh29105Tests.java b/spring-context/src/test/java/org/springframework/context/annotation/Gh29105Tests.java index 89b156a3184..099392a1152 100644 --- a/spring-context/src/test/java/org/springframework/context/annotation/Gh29105Tests.java +++ b/spring-context/src/test/java/org/springframework/context/annotation/Gh29105Tests.java @@ -44,6 +44,9 @@ class Gh29105Tests { Stream> orderedTypes = child.getBeanProvider(MyService.class).orderedStream().map(Object::getClass); assertThat(orderedTypes).containsExactly(CustomService.class, DefaultService.class); + assertThat(child.getDefaultListableBeanFactory().getOrder("defaultService")).isEqualTo(0); + assertThat(child.getDefaultListableBeanFactory().getOrder("customService")).isEqualTo(-1); + child.close(); parent.close(); } diff --git a/spring-core/src/main/java/org/springframework/core/OrderComparator.java b/spring-core/src/main/java/org/springframework/core/OrderComparator.java index 9b9a34c688d..f184f6a460b 100644 --- a/spring-core/src/main/java/org/springframework/core/OrderComparator.java +++ b/spring-core/src/main/java/org/springframework/core/OrderComparator.java @@ -95,8 +95,9 @@ public class OrderComparator implements Comparator { * using {@link #findOrder} and falls back to a regular {@link #getOrder(Object)} call. * @param obj the object to check * @return the order value, or {@code Ordered.LOWEST_PRECEDENCE} as fallback + * @since 7.0 */ - private int getOrder(@Nullable Object obj, @Nullable OrderSourceProvider sourceProvider) { + public int getOrder(@Nullable Object obj, @Nullable OrderSourceProvider sourceProvider) { Integer order = null; if (obj != null && sourceProvider != null) { Object orderSource = sourceProvider.getOrderSource(obj);