Skip to content

Commit 21f2863

Browse files
committed
ControllerAdvice resolution detects @order declared on @bean method as well
Closes gh-25872
1 parent 83bfee9 commit 21f2863

File tree

3 files changed

+73
-16
lines changed

3 files changed

+73
-16
lines changed

spring-core/src/main/java/org/springframework/core/annotation/OrderUtils.java

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2019 the original author or authors.
2+
* Copyright 2002-2020 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -81,7 +81,19 @@ public static Integer getOrder(Class<?> type, @Nullable Integer defaultOrder) {
8181
*/
8282
@Nullable
8383
public static Integer getOrder(Class<?> type) {
84-
return getOrderFromAnnotations(type, MergedAnnotations.from(type, SearchStrategy.TYPE_HIERARCHY));
84+
return getOrder((AnnotatedElement) type);
85+
}
86+
87+
/**
88+
* Return the order declared on the specified {@code element}.
89+
* <p>Takes care of {@link Order @Order} and {@code @javax.annotation.Priority}.
90+
* @param element the annotated element (e.g. type or method)
91+
* @return the order value, or {@code null} if none can be found
92+
* @since 5.3
93+
*/
94+
@Nullable
95+
public static Integer getOrder(AnnotatedElement element) {
96+
return getOrderFromAnnotations(element, MergedAnnotations.from(element, SearchStrategy.TYPE_HIERARCHY));
8597
}
8698

8799
/**

spring-web/src/main/java/org/springframework/web/method/ControllerAdviceBean.java

Lines changed: 44 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,21 @@
1616

1717
package org.springframework.web.method;
1818

19+
import java.lang.reflect.AnnotatedElement;
20+
import java.lang.reflect.Method;
1921
import java.util.ArrayList;
2022
import java.util.List;
2123

2224
import org.springframework.aop.scope.ScopedProxyUtils;
2325
import org.springframework.beans.factory.BeanFactory;
2426
import org.springframework.beans.factory.BeanFactoryUtils;
27+
import org.springframework.beans.factory.ListableBeanFactory;
28+
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
29+
import org.springframework.beans.factory.config.BeanDefinition;
30+
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
31+
import org.springframework.beans.factory.support.RootBeanDefinition;
2532
import org.springframework.context.ApplicationContext;
33+
import org.springframework.context.ConfigurableApplicationContext;
2634
import org.springframework.core.OrderComparator;
2735
import org.springframework.core.Ordered;
2836
import org.springframework.core.annotation.AnnotatedElementUtils;
@@ -128,7 +136,7 @@ public ControllerAdviceBean(String beanName, BeanFactory beanFactory, @Nullable
128136

129137
/**
130138
* Get the order value for the contained bean.
131-
* <p>As of Spring Framework 5.2, the order value is lazily retrieved using
139+
* <p>As of Spring Framework 5.3, the order value is lazily retrieved using
132140
* the following algorithm and cached. Note, however, that a
133141
* {@link ControllerAdvice @ControllerAdvice} bean that is configured as a
134142
* scoped bean &mdash; for example, as a request-scoped or session-scoped
@@ -137,6 +145,8 @@ public ControllerAdviceBean(String beanName, BeanFactory beanFactory, @Nullable
137145
* <ul>
138146
* <li>If the {@linkplain #resolveBean resolved bean} implements {@link Ordered},
139147
* use the value returned by {@link Ordered#getOrder()}.</li>
148+
* <li>If the {@linkplain org.springframework.context.annotation.Bean factory method}
149+
* is known, use the value returned by {@link OrderUtils#getOrder(AnnotatedElement)}.
140150
* <li>If the {@linkplain #getBeanType() bean type} is known, use the value returned
141151
* by {@link OrderUtils#getOrder(Class, int)} with {@link Ordered#LOWEST_PRECEDENCE}
142152
* used as the default order value.</li>
@@ -148,9 +158,10 @@ public ControllerAdviceBean(String beanName, BeanFactory beanFactory, @Nullable
148158
@Override
149159
public int getOrder() {
150160
if (this.order == null) {
161+
String beanName = null;
151162
Object resolvedBean = null;
152163
if (this.beanFactory != null && this.beanOrName instanceof String) {
153-
String beanName = (String) this.beanOrName;
164+
beanName = (String) this.beanOrName;
154165
String targetBeanName = ScopedProxyUtils.getTargetBeanName(beanName);
155166
boolean isScopedProxy = this.beanFactory.containsBean(targetBeanName);
156167
// Avoid eager @ControllerAdvice bean resolution for scoped proxies,
@@ -168,11 +179,30 @@ public int getOrder() {
168179
if (resolvedBean instanceof Ordered) {
169180
this.order = ((Ordered) resolvedBean).getOrder();
170181
}
171-
else if (this.beanType != null) {
172-
this.order = OrderUtils.getOrder(this.beanType, Ordered.LOWEST_PRECEDENCE);
173-
}
174182
else {
175-
this.order = Ordered.LOWEST_PRECEDENCE;
183+
if (beanName != null && this.beanFactory instanceof ConfigurableBeanFactory) {
184+
ConfigurableBeanFactory cbf = (ConfigurableBeanFactory) this.beanFactory;
185+
try {
186+
BeanDefinition bd = cbf.getMergedBeanDefinition(beanName);
187+
if (bd instanceof RootBeanDefinition) {
188+
Method factoryMethod = ((RootBeanDefinition) bd).getResolvedFactoryMethod();
189+
if (factoryMethod != null) {
190+
this.order = OrderUtils.getOrder(factoryMethod);
191+
}
192+
}
193+
}
194+
catch (NoSuchBeanDefinitionException ex) {
195+
// ignore -> probably a manually registered singleton
196+
}
197+
}
198+
if (this.order == null) {
199+
if (this.beanType != null) {
200+
this.order = OrderUtils.getOrder(this.beanType, Ordered.LOWEST_PRECEDENCE);
201+
}
202+
else {
203+
this.order = Ordered.LOWEST_PRECEDENCE;
204+
}
205+
}
176206
}
177207
}
178208
return this.order;
@@ -260,14 +290,19 @@ public String toString() {
260290
* @see Ordered
261291
*/
262292
public static List<ControllerAdviceBean> findAnnotatedBeans(ApplicationContext context) {
293+
ListableBeanFactory beanFactory = context;
294+
if (context instanceof ConfigurableApplicationContext) {
295+
// Use internal BeanFactory for potential downcast to ConfigurableBeanFactory above
296+
beanFactory = ((ConfigurableApplicationContext) context).getBeanFactory();
297+
}
263298
List<ControllerAdviceBean> adviceBeans = new ArrayList<>();
264-
for (String name : BeanFactoryUtils.beanNamesForTypeIncludingAncestors(context, Object.class)) {
299+
for (String name : BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory, Object.class)) {
265300
if (!ScopedProxyUtils.isScopedTarget(name)) {
266-
ControllerAdvice controllerAdvice = context.findAnnotationOnBean(name, ControllerAdvice.class);
301+
ControllerAdvice controllerAdvice = beanFactory.findAnnotationOnBean(name, ControllerAdvice.class);
267302
if (controllerAdvice != null) {
268303
// Use the @ControllerAdvice annotation found by findAnnotationOnBean()
269304
// in order to avoid a subsequent lookup of the same annotation.
270-
adviceBeans.add(new ControllerAdviceBean(name, context, controllerAdvice));
305+
adviceBeans.add(new ControllerAdviceBean(name, beanFactory, controllerAdvice));
271306
}
272307
}
273308
}

spring-web/src/test/java/org/springframework/web/method/ControllerAdviceBeanTests.java

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2019 the original author or authors.
2+
* Copyright 2002-2020 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -199,7 +199,7 @@ public void multipleMatch() {
199199
}
200200

201201
@Test
202-
@SuppressWarnings({ "rawtypes", "unchecked" })
202+
@SuppressWarnings({"rawtypes", "unchecked"})
203203
public void findAnnotatedBeansSortsBeans() {
204204
Class[] expectedTypes = {
205205
// Since ControllerAdviceBean currently treats PriorityOrdered the same as Ordered,
@@ -208,6 +208,7 @@ public void findAnnotatedBeansSortsBeans() {
208208
PriorityOrderedControllerAdvice.class,
209209
OrderAnnotationControllerAdvice.class,
210210
PriorityAnnotationControllerAdvice.class,
211+
SimpleControllerAdviceWithBeanOrder.class,
211212
SimpleControllerAdvice.class,
212213
};
213214

@@ -229,7 +230,7 @@ private void assertOrder(Object bean, int expectedOrder) {
229230
assertThat(new ControllerAdviceBean(bean).getOrder()).isEqualTo(expectedOrder);
230231
}
231232

232-
@SuppressWarnings({ "unchecked", "rawtypes" })
233+
@SuppressWarnings({"rawtypes", "unchecked"})
233234
private void assertOrder(Class beanType, int expectedOrder) {
234235
String beanName = "myBean";
235236
BeanFactory beanFactory = mock(BeanFactory.class);
@@ -261,6 +262,9 @@ private void assertNotApplicable(String message, ControllerAdviceBean controller
261262
@ControllerAdvice
262263
static class SimpleControllerAdvice {}
263264

265+
@ControllerAdvice
266+
static class SimpleControllerAdviceWithBeanOrder {}
267+
264268
@ControllerAdvice
265269
@Order(100)
266270
static class OrderAnnotationControllerAdvice {}
@@ -321,12 +325,12 @@ static class ShouldNotMatch {}
321325
static class MarkerClass {}
322326

323327
@Retention(RetentionPolicy.RUNTIME)
324-
static @interface ControllerAnnotation {}
328+
@interface ControllerAnnotation {}
325329

326330
@ControllerAnnotation
327331
public static class AnnotatedController {}
328332

329-
static interface ControllerInterface {}
333+
interface ControllerInterface {}
330334

331335
static class ImplementationController implements ControllerInterface {}
332336

@@ -342,6 +346,12 @@ SimpleControllerAdvice simpleControllerAdvice() {
342346
return new SimpleControllerAdvice();
343347
}
344348

349+
@Bean
350+
@Order(300)
351+
SimpleControllerAdviceWithBeanOrder simpleControllerAdviceWithBeanOrder() {
352+
return new SimpleControllerAdviceWithBeanOrder();
353+
}
354+
345355
@Bean
346356
OrderAnnotationControllerAdvice orderAnnotationControllerAdvice() {
347357
return new OrderAnnotationControllerAdvice();

0 commit comments

Comments
 (0)