diff --git a/spring-web/src/main/java/org/springframework/web/bind/annotation/ControllerAdvice.java b/spring-web/src/main/java/org/springframework/web/bind/annotation/ControllerAdvice.java index dd59a414555..db1f6c6e4cf 100644 --- a/spring-web/src/main/java/org/springframework/web/bind/annotation/ControllerAdvice.java +++ b/spring-web/src/main/java/org/springframework/web/bind/annotation/ControllerAdvice.java @@ -33,15 +33,15 @@ import org.springframework.stereotype.Component; * multiple {@code @Controller} classes. * *

Classes with {@code @ControllerAdvice} can be declared explicitly as Spring - * beans or auto-detected via classpath scanning. All such beans are sorted via - * {@link org.springframework.core.annotation.AnnotationAwareOrderComparator - * AnnotationAwareOrderComparator}, based on - * {@link org.springframework.core.annotation.Order @Order} and - * {@link org.springframework.core.Ordered Ordered}, and applied in that order - * at runtime. For handling exceptions, an {@code @ExceptionHandler} will be - * picked on the first advice with a matching exception handler method. For - * model attributes and {@code InitBinder} initialization, {@code @ModelAttribute} - * and {@code @InitBinder} methods will also follow {@code @ControllerAdvice} order. + * beans or auto-detected via classpath scanning. All such beans are sorted based + * on {@link org.springframework.core.Ordered Ordered}, + * {@link org.springframework.core.annotation.Order @Order}, and + * {@link javax.annotation.Priority @Priority} (in that order of precedence), + * and applied in that order at runtime. For handling exceptions, an + * {@code @ExceptionHandler} will be picked on the first advice with a matching + * exception handler method. For model attributes and {@code InitBinder} + * initialization, {@code @ModelAttribute} and {@code @InitBinder} methods will + * also follow {@code @ControllerAdvice} order. * *

Note: For {@code @ExceptionHandler} methods, a root exception match will be * preferred to just matching a cause of the current exception, among the handler diff --git a/spring-web/src/main/java/org/springframework/web/method/ControllerAdviceBean.java b/spring-web/src/main/java/org/springframework/web/method/ControllerAdviceBean.java index 5308092ad73..0c54d039fab 100644 --- a/spring-web/src/main/java/org/springframework/web/method/ControllerAdviceBean.java +++ b/spring-web/src/main/java/org/springframework/web/method/ControllerAdviceBean.java @@ -67,7 +67,8 @@ public class ControllerAdviceBean implements Ordered { @Nullable private final BeanFactory beanFactory; - private final int order; + @Nullable + private Integer order; /** @@ -81,7 +82,6 @@ public class ControllerAdviceBean implements Ordered { this.beanType = ClassUtils.getUserClass(bean.getClass()); this.beanTypePredicate = createBeanTypePredicate(this.beanType); this.beanFactory = null; - this.order = initOrderFromBean(bean); } /** @@ -117,16 +117,32 @@ public class ControllerAdviceBean implements Ordered { this.beanTypePredicate = (controllerAdvice != null ? createBeanTypePredicate(controllerAdvice) : createBeanTypePredicate(this.beanType)); this.beanFactory = beanFactory; - this.order = initOrderFromBeanType(this.beanType); } /** - * Return the order value extracted from the {@link ControllerAdvice} - * annotation, or {@link Ordered#LOWEST_PRECEDENCE} otherwise. + * Get the order value for the contained bean. + *

As of Spring Framework 5.2, the order value is lazily retrieved using + * the following algorithm and cached. + *

+ * @see #resolveBean() */ @Override public int getOrder() { + if (this.order == null) { + Object resolvedBean = resolveBean(); + if (resolvedBean instanceof Ordered) { + this.order = ((Ordered) resolvedBean).getOrder(); + } + else { + this.order = OrderUtils.getOrder(getBeanType(), Ordered.LOWEST_PRECEDENCE); + } + } return this.order; } @@ -236,16 +252,4 @@ public class ControllerAdviceBean implements Ordered { return HandlerTypePredicate.forAnyHandlerType(); } - private static int initOrderFromBean(Object bean) { - return (bean instanceof Ordered ? ((Ordered) bean).getOrder() : initOrderFromBeanType(bean.getClass())); - } - - private static int initOrderFromBeanType(@Nullable Class beanType) { - Integer order = null; - if (beanType != null) { - order = OrderUtils.getOrder(beanType); - } - return (order != null ? order : Ordered.LOWEST_PRECEDENCE); - } - } diff --git a/spring-web/src/test/java/org/springframework/web/method/ControllerAdviceBeanTests.java b/spring-web/src/test/java/org/springframework/web/method/ControllerAdviceBeanTests.java index 3810ea41548..4236c8256d0 100644 --- a/spring-web/src/test/java/org/springframework/web/method/ControllerAdviceBeanTests.java +++ b/spring-web/src/test/java/org/springframework/web/method/ControllerAdviceBeanTests.java @@ -22,6 +22,7 @@ import javax.annotation.Priority; import org.junit.Test; +import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.BeanFactory; import org.springframework.core.Ordered; import org.springframework.core.annotation.Order; @@ -32,7 +33,6 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; /** @@ -103,8 +103,7 @@ public class ControllerAdviceBeanTests { @Test public void orderedViaOrderedInterfaceForBeanName() { - // TODO Change expectedOrder once Ordered is properly supported - assertOrder(OrderedControllerAdvice.class, Ordered.LOWEST_PRECEDENCE); + assertOrder(OrderedControllerAdvice.class, 42); } @Test @@ -211,15 +210,14 @@ public class ControllerAdviceBeanTests { BeanFactory beanFactory = mock(BeanFactory.class); given(beanFactory.containsBean(beanName)).willReturn(true); given(beanFactory.getType(beanName)).willReturn(beanType); + given(beanFactory.getBean(beanName)).willReturn(BeanUtils.instantiateClass(beanType)); ControllerAdviceBean controllerAdviceBean = new ControllerAdviceBean(beanName, beanFactory); assertThat(controllerAdviceBean.getOrder()).isEqualTo(expectedOrder); verify(beanFactory).containsBean(beanName); verify(beanFactory).getType(beanName); - // Expecting 0 invocations of getBean() since Ordered is not yet supported. - // TODO Change expected number of invocations once Ordered is properly supported - verify(beanFactory, times(0)).getBean(beanName); + verify(beanFactory).getBean(beanName); } private void assertApplicable(String message, ControllerAdviceBean controllerAdvice, Class controllerBeanType) {