ControllerAdvice resolution detects @Order declared on @Bean method as well
Closes gh-25872
This commit is contained in:
parent
83bfee9201
commit
21f2863d8e
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2019 the original author or authors.
|
* Copyright 2002-2020 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -81,7 +81,19 @@ public abstract class OrderUtils {
|
||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
public static Integer getOrder(Class<?> type) {
|
public static Integer getOrder(Class<?> type) {
|
||||||
return getOrderFromAnnotations(type, MergedAnnotations.from(type, SearchStrategy.TYPE_HIERARCHY));
|
return getOrder((AnnotatedElement) type);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the order declared on the specified {@code element}.
|
||||||
|
* <p>Takes care of {@link Order @Order} and {@code @javax.annotation.Priority}.
|
||||||
|
* @param element the annotated element (e.g. type or method)
|
||||||
|
* @return the order value, or {@code null} if none can be found
|
||||||
|
* @since 5.3
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
public static Integer getOrder(AnnotatedElement element) {
|
||||||
|
return getOrderFromAnnotations(element, MergedAnnotations.from(element, SearchStrategy.TYPE_HIERARCHY));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -16,13 +16,21 @@
|
||||||
|
|
||||||
package org.springframework.web.method;
|
package org.springframework.web.method;
|
||||||
|
|
||||||
|
import java.lang.reflect.AnnotatedElement;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.springframework.aop.scope.ScopedProxyUtils;
|
import org.springframework.aop.scope.ScopedProxyUtils;
|
||||||
import org.springframework.beans.factory.BeanFactory;
|
import org.springframework.beans.factory.BeanFactory;
|
||||||
import org.springframework.beans.factory.BeanFactoryUtils;
|
import org.springframework.beans.factory.BeanFactoryUtils;
|
||||||
|
import org.springframework.beans.factory.ListableBeanFactory;
|
||||||
|
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||||
|
import org.springframework.beans.factory.config.BeanDefinition;
|
||||||
|
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
||||||
|
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||||
import org.springframework.context.ApplicationContext;
|
import org.springframework.context.ApplicationContext;
|
||||||
|
import org.springframework.context.ConfigurableApplicationContext;
|
||||||
import org.springframework.core.OrderComparator;
|
import org.springframework.core.OrderComparator;
|
||||||
import org.springframework.core.Ordered;
|
import org.springframework.core.Ordered;
|
||||||
import org.springframework.core.annotation.AnnotatedElementUtils;
|
import org.springframework.core.annotation.AnnotatedElementUtils;
|
||||||
|
@ -128,7 +136,7 @@ public class ControllerAdviceBean implements Ordered {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the order value for the contained bean.
|
* Get the order value for the contained bean.
|
||||||
* <p>As of Spring Framework 5.2, the order value is lazily retrieved using
|
* <p>As of Spring Framework 5.3, the order value is lazily retrieved using
|
||||||
* the following algorithm and cached. Note, however, that a
|
* the following algorithm and cached. Note, however, that a
|
||||||
* {@link ControllerAdvice @ControllerAdvice} bean that is configured as a
|
* {@link ControllerAdvice @ControllerAdvice} bean that is configured as a
|
||||||
* scoped bean — for example, as a request-scoped or session-scoped
|
* scoped bean — for example, as a request-scoped or session-scoped
|
||||||
|
@ -137,6 +145,8 @@ public class ControllerAdviceBean implements Ordered {
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>If the {@linkplain #resolveBean resolved bean} implements {@link Ordered},
|
* <li>If the {@linkplain #resolveBean resolved bean} implements {@link Ordered},
|
||||||
* use the value returned by {@link Ordered#getOrder()}.</li>
|
* use the value returned by {@link Ordered#getOrder()}.</li>
|
||||||
|
* <li>If the {@linkplain org.springframework.context.annotation.Bean factory method}
|
||||||
|
* is known, use the value returned by {@link OrderUtils#getOrder(AnnotatedElement)}.
|
||||||
* <li>If the {@linkplain #getBeanType() bean type} is known, use the value returned
|
* <li>If the {@linkplain #getBeanType() bean type} is known, use the value returned
|
||||||
* by {@link OrderUtils#getOrder(Class, int)} with {@link Ordered#LOWEST_PRECEDENCE}
|
* by {@link OrderUtils#getOrder(Class, int)} with {@link Ordered#LOWEST_PRECEDENCE}
|
||||||
* used as the default order value.</li>
|
* used as the default order value.</li>
|
||||||
|
@ -148,9 +158,10 @@ public class ControllerAdviceBean implements Ordered {
|
||||||
@Override
|
@Override
|
||||||
public int getOrder() {
|
public int getOrder() {
|
||||||
if (this.order == null) {
|
if (this.order == null) {
|
||||||
|
String beanName = null;
|
||||||
Object resolvedBean = null;
|
Object resolvedBean = null;
|
||||||
if (this.beanFactory != null && this.beanOrName instanceof String) {
|
if (this.beanFactory != null && this.beanOrName instanceof String) {
|
||||||
String beanName = (String) this.beanOrName;
|
beanName = (String) this.beanOrName;
|
||||||
String targetBeanName = ScopedProxyUtils.getTargetBeanName(beanName);
|
String targetBeanName = ScopedProxyUtils.getTargetBeanName(beanName);
|
||||||
boolean isScopedProxy = this.beanFactory.containsBean(targetBeanName);
|
boolean isScopedProxy = this.beanFactory.containsBean(targetBeanName);
|
||||||
// Avoid eager @ControllerAdvice bean resolution for scoped proxies,
|
// Avoid eager @ControllerAdvice bean resolution for scoped proxies,
|
||||||
|
@ -168,11 +179,30 @@ public class ControllerAdviceBean implements Ordered {
|
||||||
if (resolvedBean instanceof Ordered) {
|
if (resolvedBean instanceof Ordered) {
|
||||||
this.order = ((Ordered) resolvedBean).getOrder();
|
this.order = ((Ordered) resolvedBean).getOrder();
|
||||||
}
|
}
|
||||||
else if (this.beanType != null) {
|
|
||||||
this.order = OrderUtils.getOrder(this.beanType, Ordered.LOWEST_PRECEDENCE);
|
|
||||||
}
|
|
||||||
else {
|
else {
|
||||||
this.order = Ordered.LOWEST_PRECEDENCE;
|
if (beanName != null && this.beanFactory instanceof ConfigurableBeanFactory) {
|
||||||
|
ConfigurableBeanFactory cbf = (ConfigurableBeanFactory) this.beanFactory;
|
||||||
|
try {
|
||||||
|
BeanDefinition bd = cbf.getMergedBeanDefinition(beanName);
|
||||||
|
if (bd instanceof RootBeanDefinition) {
|
||||||
|
Method factoryMethod = ((RootBeanDefinition) bd).getResolvedFactoryMethod();
|
||||||
|
if (factoryMethod != null) {
|
||||||
|
this.order = OrderUtils.getOrder(factoryMethod);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (NoSuchBeanDefinitionException ex) {
|
||||||
|
// ignore -> probably a manually registered singleton
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (this.order == null) {
|
||||||
|
if (this.beanType != null) {
|
||||||
|
this.order = OrderUtils.getOrder(this.beanType, Ordered.LOWEST_PRECEDENCE);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.order = Ordered.LOWEST_PRECEDENCE;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return this.order;
|
return this.order;
|
||||||
|
@ -260,14 +290,19 @@ public class ControllerAdviceBean implements Ordered {
|
||||||
* @see Ordered
|
* @see Ordered
|
||||||
*/
|
*/
|
||||||
public static List<ControllerAdviceBean> findAnnotatedBeans(ApplicationContext context) {
|
public static List<ControllerAdviceBean> findAnnotatedBeans(ApplicationContext context) {
|
||||||
|
ListableBeanFactory beanFactory = context;
|
||||||
|
if (context instanceof ConfigurableApplicationContext) {
|
||||||
|
// Use internal BeanFactory for potential downcast to ConfigurableBeanFactory above
|
||||||
|
beanFactory = ((ConfigurableApplicationContext) context).getBeanFactory();
|
||||||
|
}
|
||||||
List<ControllerAdviceBean> adviceBeans = new ArrayList<>();
|
List<ControllerAdviceBean> adviceBeans = new ArrayList<>();
|
||||||
for (String name : BeanFactoryUtils.beanNamesForTypeIncludingAncestors(context, Object.class)) {
|
for (String name : BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory, Object.class)) {
|
||||||
if (!ScopedProxyUtils.isScopedTarget(name)) {
|
if (!ScopedProxyUtils.isScopedTarget(name)) {
|
||||||
ControllerAdvice controllerAdvice = context.findAnnotationOnBean(name, ControllerAdvice.class);
|
ControllerAdvice controllerAdvice = beanFactory.findAnnotationOnBean(name, ControllerAdvice.class);
|
||||||
if (controllerAdvice != null) {
|
if (controllerAdvice != null) {
|
||||||
// Use the @ControllerAdvice annotation found by findAnnotationOnBean()
|
// Use the @ControllerAdvice annotation found by findAnnotationOnBean()
|
||||||
// in order to avoid a subsequent lookup of the same annotation.
|
// in order to avoid a subsequent lookup of the same annotation.
|
||||||
adviceBeans.add(new ControllerAdviceBean(name, context, controllerAdvice));
|
adviceBeans.add(new ControllerAdviceBean(name, beanFactory, controllerAdvice));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2019 the original author or authors.
|
* Copyright 2002-2020 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -199,7 +199,7 @@ public class ControllerAdviceBeanTests {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||||
public void findAnnotatedBeansSortsBeans() {
|
public void findAnnotatedBeansSortsBeans() {
|
||||||
Class[] expectedTypes = {
|
Class[] expectedTypes = {
|
||||||
// Since ControllerAdviceBean currently treats PriorityOrdered the same as Ordered,
|
// Since ControllerAdviceBean currently treats PriorityOrdered the same as Ordered,
|
||||||
|
@ -208,6 +208,7 @@ public class ControllerAdviceBeanTests {
|
||||||
PriorityOrderedControllerAdvice.class,
|
PriorityOrderedControllerAdvice.class,
|
||||||
OrderAnnotationControllerAdvice.class,
|
OrderAnnotationControllerAdvice.class,
|
||||||
PriorityAnnotationControllerAdvice.class,
|
PriorityAnnotationControllerAdvice.class,
|
||||||
|
SimpleControllerAdviceWithBeanOrder.class,
|
||||||
SimpleControllerAdvice.class,
|
SimpleControllerAdvice.class,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -229,7 +230,7 @@ public class ControllerAdviceBeanTests {
|
||||||
assertThat(new ControllerAdviceBean(bean).getOrder()).isEqualTo(expectedOrder);
|
assertThat(new ControllerAdviceBean(bean).getOrder()).isEqualTo(expectedOrder);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||||
private void assertOrder(Class beanType, int expectedOrder) {
|
private void assertOrder(Class beanType, int expectedOrder) {
|
||||||
String beanName = "myBean";
|
String beanName = "myBean";
|
||||||
BeanFactory beanFactory = mock(BeanFactory.class);
|
BeanFactory beanFactory = mock(BeanFactory.class);
|
||||||
|
@ -261,6 +262,9 @@ public class ControllerAdviceBeanTests {
|
||||||
@ControllerAdvice
|
@ControllerAdvice
|
||||||
static class SimpleControllerAdvice {}
|
static class SimpleControllerAdvice {}
|
||||||
|
|
||||||
|
@ControllerAdvice
|
||||||
|
static class SimpleControllerAdviceWithBeanOrder {}
|
||||||
|
|
||||||
@ControllerAdvice
|
@ControllerAdvice
|
||||||
@Order(100)
|
@Order(100)
|
||||||
static class OrderAnnotationControllerAdvice {}
|
static class OrderAnnotationControllerAdvice {}
|
||||||
|
@ -321,12 +325,12 @@ public class ControllerAdviceBeanTests {
|
||||||
static class MarkerClass {}
|
static class MarkerClass {}
|
||||||
|
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
static @interface ControllerAnnotation {}
|
@interface ControllerAnnotation {}
|
||||||
|
|
||||||
@ControllerAnnotation
|
@ControllerAnnotation
|
||||||
public static class AnnotatedController {}
|
public static class AnnotatedController {}
|
||||||
|
|
||||||
static interface ControllerInterface {}
|
interface ControllerInterface {}
|
||||||
|
|
||||||
static class ImplementationController implements ControllerInterface {}
|
static class ImplementationController implements ControllerInterface {}
|
||||||
|
|
||||||
|
@ -342,6 +346,12 @@ public class ControllerAdviceBeanTests {
|
||||||
return new SimpleControllerAdvice();
|
return new SimpleControllerAdvice();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@Order(300)
|
||||||
|
SimpleControllerAdviceWithBeanOrder simpleControllerAdviceWithBeanOrder() {
|
||||||
|
return new SimpleControllerAdviceWithBeanOrder();
|
||||||
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
OrderAnnotationControllerAdvice orderAnnotationControllerAdvice() {
|
OrderAnnotationControllerAdvice orderAnnotationControllerAdvice() {
|
||||||
return new OrderAnnotationControllerAdvice();
|
return new OrderAnnotationControllerAdvice();
|
||||||
|
|
Loading…
Reference in New Issue