Introduce ORDER_ATTRIBUTE on AbstractBeanDefinition
This commit allows to define a bean order programmatically at bean definition level (functional equivalent of `@Order`). If specified, the order attribute defined at bean definition level overrides potential values set with `@Order`. See gh-30849
This commit is contained in:
parent
a506238ef6
commit
16ac495084
|
@ -51,6 +51,7 @@ import org.springframework.util.StringUtils;
|
||||||
* @author Juergen Hoeller
|
* @author Juergen Hoeller
|
||||||
* @author Rob Harrop
|
* @author Rob Harrop
|
||||||
* @author Mark Fisher
|
* @author Mark Fisher
|
||||||
|
* @author Sebastien Deleuze
|
||||||
* @see GenericBeanDefinition
|
* @see GenericBeanDefinition
|
||||||
* @see RootBeanDefinition
|
* @see RootBeanDefinition
|
||||||
* @see ChildBeanDefinition
|
* @see ChildBeanDefinition
|
||||||
|
@ -139,6 +140,18 @@ public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccess
|
||||||
*/
|
*/
|
||||||
public static final String PREFERRED_CONSTRUCTORS_ATTRIBUTE = "preferredConstructors";
|
public static final String PREFERRED_CONSTRUCTORS_ATTRIBUTE = "preferredConstructors";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name of an attribute that can be
|
||||||
|
* {@link org.springframework.core.AttributeAccessor#setAttribute set} on a
|
||||||
|
* {@link org.springframework.beans.factory.config.BeanDefinition} so that
|
||||||
|
* bean definitions can indicate the sort order for the targeted bean.
|
||||||
|
* This is analogous to the {@code @Order} annotation.
|
||||||
|
* @since 6.1.2
|
||||||
|
* @see org.springframework.core.annotation.Order
|
||||||
|
* @see org.springframework.core.Ordered
|
||||||
|
*/
|
||||||
|
public static final String ORDER_ATTRIBUTE = "order";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constant that indicates the container should attempt to infer the
|
* Constant that indicates the container should attempt to infer the
|
||||||
* {@link #setDestroyMethodName destroy method name} for a bean as opposed to
|
* {@link #setDestroyMethodName destroy method name} for a bean as opposed to
|
||||||
|
|
|
@ -70,6 +70,7 @@ import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||||
import org.springframework.beans.factory.config.DependencyDescriptor;
|
import org.springframework.beans.factory.config.DependencyDescriptor;
|
||||||
import org.springframework.beans.factory.config.NamedBeanHolder;
|
import org.springframework.beans.factory.config.NamedBeanHolder;
|
||||||
import org.springframework.core.OrderComparator;
|
import org.springframework.core.OrderComparator;
|
||||||
|
import org.springframework.core.Ordered;
|
||||||
import org.springframework.core.ResolvableType;
|
import org.springframework.core.ResolvableType;
|
||||||
import org.springframework.core.annotation.MergedAnnotation;
|
import org.springframework.core.annotation.MergedAnnotation;
|
||||||
import org.springframework.core.annotation.MergedAnnotations;
|
import org.springframework.core.annotation.MergedAnnotations;
|
||||||
|
@ -110,6 +111,7 @@ import org.springframework.util.StringUtils;
|
||||||
* @author Chris Beams
|
* @author Chris Beams
|
||||||
* @author Phillip Webb
|
* @author Phillip Webb
|
||||||
* @author Stephane Nicoll
|
* @author Stephane Nicoll
|
||||||
|
* @author Sebastien Deleuze
|
||||||
* @since 16 April 2001
|
* @since 16 April 2001
|
||||||
* @see #registerBeanDefinition
|
* @see #registerBeanDefinition
|
||||||
* @see #addBeanPostProcessor
|
* @see #addBeanPostProcessor
|
||||||
|
@ -2230,7 +2232,9 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
||||||
* that is aware of the bean metadata of the instances to sort.
|
* that is aware of the bean metadata of the instances to sort.
|
||||||
* <p>Lookup for the method factory of an instance to sort, if any, and let the
|
* <p>Lookup for the method factory of an instance to sort, if any, and let the
|
||||||
* comparator retrieve the {@link org.springframework.core.annotation.Order}
|
* comparator retrieve the {@link org.springframework.core.annotation.Order}
|
||||||
* value defined on it. This essentially allows for the following construct:
|
* value defined on it.
|
||||||
|
* <p>As of 6.1.2, this class takes the {@link AbstractBeanDefinition#ORDER_ATTRIBUTE}
|
||||||
|
* attribute into account.
|
||||||
*/
|
*/
|
||||||
private class FactoryAwareOrderSourceProvider implements OrderComparator.OrderSourceProvider {
|
private class FactoryAwareOrderSourceProvider implements OrderComparator.OrderSourceProvider {
|
||||||
|
|
||||||
|
@ -2249,7 +2253,17 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
RootBeanDefinition beanDefinition = (RootBeanDefinition) getMergedBeanDefinition(beanName);
|
RootBeanDefinition beanDefinition = (RootBeanDefinition) getMergedBeanDefinition(beanName);
|
||||||
List<Object> sources = new ArrayList<>(2);
|
List<Object> sources = new ArrayList<>(3);
|
||||||
|
Object orderAttribute = beanDefinition.getAttribute(AbstractBeanDefinition.ORDER_ATTRIBUTE);
|
||||||
|
if (orderAttribute != null) {
|
||||||
|
if (orderAttribute instanceof Integer order) {
|
||||||
|
sources.add((Ordered) () -> order);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw new IllegalStateException("Invalid value type for attribute '" +
|
||||||
|
AbstractBeanDefinition.ORDER_ATTRIBUTE + "': " + orderAttribute.getClass().getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
Method factoryMethod = beanDefinition.getResolvedFactoryMethod();
|
Method factoryMethod = beanDefinition.getResolvedFactoryMethod();
|
||||||
if (factoryMethod != null) {
|
if (factoryMethod != null) {
|
||||||
sources.add(factoryMethod);
|
sources.add(factoryMethod);
|
||||||
|
|
|
@ -79,9 +79,11 @@ import org.springframework.beans.testfixture.beans.TestBean;
|
||||||
import org.springframework.beans.testfixture.beans.factory.DummyFactory;
|
import org.springframework.beans.testfixture.beans.factory.DummyFactory;
|
||||||
import org.springframework.core.DefaultParameterNameDiscoverer;
|
import org.springframework.core.DefaultParameterNameDiscoverer;
|
||||||
import org.springframework.core.MethodParameter;
|
import org.springframework.core.MethodParameter;
|
||||||
|
import org.springframework.core.Ordered;
|
||||||
import org.springframework.core.ParameterNameDiscoverer;
|
import org.springframework.core.ParameterNameDiscoverer;
|
||||||
import org.springframework.core.ResolvableType;
|
import org.springframework.core.ResolvableType;
|
||||||
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
|
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
|
||||||
|
import org.springframework.core.annotation.Order;
|
||||||
import org.springframework.core.convert.support.DefaultConversionService;
|
import org.springframework.core.convert.support.DefaultConversionService;
|
||||||
import org.springframework.core.convert.support.GenericConversionService;
|
import org.springframework.core.convert.support.GenericConversionService;
|
||||||
import org.springframework.core.io.Resource;
|
import org.springframework.core.io.Resource;
|
||||||
|
@ -1484,6 +1486,55 @@ class DefaultListableBeanFactoryTests {
|
||||||
assertThat(bean.getSpouse2()).isNull();
|
assertThat(bean.getSpouse2()).isNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void orderFromAttribute() {
|
||||||
|
GenericBeanDefinition bd1 = new GenericBeanDefinition();
|
||||||
|
bd1.setBeanClass(TestBean.class);
|
||||||
|
bd1.setPropertyValues(new MutablePropertyValues(List.of(new PropertyValue("name", "lowest"))));
|
||||||
|
bd1.setAttribute(AbstractBeanDefinition.ORDER_ATTRIBUTE, Ordered.LOWEST_PRECEDENCE);
|
||||||
|
lbf.registerBeanDefinition("bean1", bd1);
|
||||||
|
GenericBeanDefinition bd2 = new GenericBeanDefinition();
|
||||||
|
bd2.setBeanClass(TestBean.class);
|
||||||
|
bd2.setPropertyValues(new MutablePropertyValues(List.of(new PropertyValue("name", "highest"))));
|
||||||
|
bd2.setAttribute(AbstractBeanDefinition.ORDER_ATTRIBUTE, Ordered.HIGHEST_PRECEDENCE);
|
||||||
|
lbf.registerBeanDefinition("bean2", bd2);
|
||||||
|
assertThat(lbf.getBeanProvider(TestBean.class).orderedStream().map(TestBean::getName))
|
||||||
|
.containsExactly("highest", "lowest");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void orderFromAttributeOverrideAnnotation() {
|
||||||
|
lbf.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);
|
||||||
|
RootBeanDefinition rbd1 = new RootBeanDefinition(LowestPrecedenceTestBeanFactoryBean.class);
|
||||||
|
rbd1.setAttribute(AbstractBeanDefinition.ORDER_ATTRIBUTE, Ordered.HIGHEST_PRECEDENCE);
|
||||||
|
lbf.registerBeanDefinition("lowestPrecedenceFactory", rbd1);
|
||||||
|
RootBeanDefinition rbd2 = new RootBeanDefinition(HighestPrecedenceTestBeanFactoryBean.class);
|
||||||
|
rbd2.setAttribute(AbstractBeanDefinition.ORDER_ATTRIBUTE, Ordered.LOWEST_PRECEDENCE);
|
||||||
|
lbf.registerBeanDefinition("highestPrecedenceFactory", rbd2);
|
||||||
|
GenericBeanDefinition bd1 = new GenericBeanDefinition();
|
||||||
|
bd1.setFactoryBeanName("highestPrecedenceFactory");
|
||||||
|
lbf.registerBeanDefinition("bean1", bd1);
|
||||||
|
GenericBeanDefinition bd2 = new GenericBeanDefinition();
|
||||||
|
bd2.setFactoryBeanName("lowestPrecedenceFactory");
|
||||||
|
lbf.registerBeanDefinition("bean2", bd2);
|
||||||
|
assertThat(lbf.getBeanProvider(TestBean.class).orderedStream().map(TestBean::getName))
|
||||||
|
.containsExactly("fromLowestPrecedenceTestBeanFactoryBean", "fromHighestPrecedenceTestBeanFactoryBean");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void invalidOrderAttribute() {
|
||||||
|
GenericBeanDefinition bd1 = new GenericBeanDefinition();
|
||||||
|
bd1.setBeanClass(TestBean.class);
|
||||||
|
bd1.setAttribute(AbstractBeanDefinition.ORDER_ATTRIBUTE, Boolean.TRUE);
|
||||||
|
lbf.registerBeanDefinition("bean1", bd1);
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void dependsOnCycle() {
|
void dependsOnCycle() {
|
||||||
RootBeanDefinition bd1 = new RootBeanDefinition(TestBean.class);
|
RootBeanDefinition bd1 = new RootBeanDefinition(TestBean.class);
|
||||||
|
@ -3426,4 +3477,34 @@ class DefaultListableBeanFactoryTests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Order
|
||||||
|
private static class LowestPrecedenceTestBeanFactoryBean implements FactoryBean<TestBean> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TestBean getObject() {
|
||||||
|
return new TestBean("fromLowestPrecedenceTestBeanFactoryBean");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<?> getObjectType() {
|
||||||
|
return TestBean.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Order(Ordered.HIGHEST_PRECEDENCE)
|
||||||
|
private static class HighestPrecedenceTestBeanFactoryBean implements FactoryBean<TestBean> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TestBean getObject() {
|
||||||
|
return new TestBean("fromHighestPrecedenceTestBeanFactoryBean");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<?> getObjectType() {
|
||||||
|
return TestBean.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue