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 b7d1701a375..20e4207c462 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 @@ -33,7 +33,6 @@ import org.springframework.context.ApplicationContext; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.core.OrderComparator; import org.springframework.core.Ordered; -import org.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.core.annotation.OrderUtils; import org.springframework.lang.Nullable; import org.springframework.util.Assert; @@ -43,10 +42,11 @@ import org.springframework.web.bind.annotation.ControllerAdvice; /** * Encapsulates information about an {@link ControllerAdvice @ControllerAdvice} * Spring-managed bean without necessarily requiring it to be instantiated. + * The {@link #findAnnotatedBeans(ApplicationContext)} method can be used to + * discover such beans. * - *

The {@link #findAnnotatedBeans(ApplicationContext)} method can be used to - * discover such beans. However, a {@code ControllerAdviceBean} may be created - * from any object, including ones without an {@code @ControllerAdvice} annotation. + *

This class is internal to Spring Framework and is not meant to be used + * by applications to manually create {@code @ControllerAdvice} beans. * * @author Rossen Stoyanchev * @author Brian Clozel @@ -56,11 +56,7 @@ import org.springframework.web.bind.annotation.ControllerAdvice; */ public class ControllerAdviceBean implements Ordered { - /** - * Reference to the actual bean instance or a {@code String} representing - * the bean name. - */ - private final Object beanOrName; + private final String beanName; private final boolean isSingleton; @@ -76,38 +72,12 @@ public class ControllerAdviceBean implements Ordered { private final HandlerTypePredicate beanTypePredicate; - @Nullable private final BeanFactory beanFactory; @Nullable private Integer order; - /** - * Create a {@code ControllerAdviceBean} using the given bean instance. - * @param bean the bean instance - */ - public ControllerAdviceBean(Object bean) { - Assert.notNull(bean, "Bean must not be null"); - this.beanOrName = bean; - this.isSingleton = true; - this.resolvedBean = bean; - this.beanType = ClassUtils.getUserClass(bean.getClass()); - this.beanTypePredicate = createBeanTypePredicate(this.beanType); - this.beanFactory = null; - } - - /** - * Create a {@code ControllerAdviceBean} using the given bean name and - * {@code BeanFactory}. - * @param beanName the name of the bean - * @param beanFactory a {@code BeanFactory} to retrieve the bean type initially - * and later to resolve the actual bean - */ - public ControllerAdviceBean(String beanName, BeanFactory beanFactory) { - this(beanName, beanFactory, null); - } - /** * Create a {@code ControllerAdviceBean} using the given bean name, * {@code BeanFactory}, and {@link ControllerAdvice @ControllerAdvice} @@ -115,21 +85,20 @@ public class ControllerAdviceBean implements Ordered { * @param beanName the name of the bean * @param beanFactory a {@code BeanFactory} to retrieve the bean type initially * and later to resolve the actual bean - * @param controllerAdvice the {@code @ControllerAdvice} annotation for the - * bean, or {@code null} if not yet retrieved + * @param controllerAdvice the {@code @ControllerAdvice} annotation for the bean * @since 5.2 */ - public ControllerAdviceBean(String beanName, BeanFactory beanFactory, @Nullable ControllerAdvice controllerAdvice) { + public ControllerAdviceBean(String beanName, BeanFactory beanFactory, ControllerAdvice controllerAdvice) { Assert.hasText(beanName, "Bean name must contain text"); Assert.notNull(beanFactory, "BeanFactory must not be null"); Assert.isTrue(beanFactory.containsBean(beanName), () -> "BeanFactory [" + beanFactory + "] does not contain specified controller advice bean '" + beanName + "'"); + Assert.notNull(controllerAdvice, "ControllerAdvice must not be null"); - this.beanOrName = beanName; + this.beanName = beanName; this.isSingleton = beanFactory.isSingleton(beanName); this.beanType = getBeanType(beanName, beanFactory); - this.beanTypePredicate = (controllerAdvice != null ? createBeanTypePredicate(controllerAdvice) : - createBeanTypePredicate(this.beanType)); + this.beanTypePredicate = createBeanTypePredicate(controllerAdvice); this.beanFactory = beanFactory; } @@ -158,21 +127,14 @@ public class ControllerAdviceBean implements Ordered { @Override public int getOrder() { if (this.order == null) { - String beanName = null; Object resolvedBean = null; - if (this.beanFactory != null && this.beanOrName instanceof String stringBeanName) { - beanName = stringBeanName; - String targetBeanName = ScopedProxyUtils.getTargetBeanName(beanName); - boolean isScopedProxy = this.beanFactory.containsBean(targetBeanName); - // Avoid eager @ControllerAdvice bean resolution for scoped proxies, - // since attempting to do so during context initialization would result - // in an exception due to the current absence of the scope. For example, - // an HTTP request or session scope is not active during initialization. - if (!isScopedProxy && !ScopedProxyUtils.isScopedTarget(beanName)) { - resolvedBean = resolveBean(); - } - } - else { + String targetBeanName = ScopedProxyUtils.getTargetBeanName(this.beanName); + boolean isScopedProxy = this.beanFactory.containsBean(targetBeanName); + // Avoid eager @ControllerAdvice bean resolution for scoped proxies, + // since attempting to do so during context initialization would result + // in an exception due to the current absence of the scope. For example, + // an HTTP request or session scope is not active during initialization. + if (!isScopedProxy && !ScopedProxyUtils.isScopedTarget(this.beanName)) { resolvedBean = resolveBean(); } @@ -180,9 +142,9 @@ public class ControllerAdviceBean implements Ordered { this.order = ordered.getOrder(); } else { - if (beanName != null && this.beanFactory instanceof ConfigurableBeanFactory cbf) { + if (this.beanFactory instanceof ConfigurableBeanFactory cbf) { try { - BeanDefinition bd = cbf.getMergedBeanDefinition(beanName); + BeanDefinition bd = cbf.getMergedBeanDefinition(this.beanName); if (bd instanceof RootBeanDefinition rbd) { Method factoryMethod = rbd.getResolvedFactoryMethod(); if (factoryMethod != null) { @@ -225,9 +187,7 @@ public class ControllerAdviceBean implements Ordered { */ public Object resolveBean() { if (this.resolvedBean == null) { - // this.beanOrName must be a String representing the bean name if - // this.resolvedBean is null. - Object resolvedBean = obtainBeanFactory().getBean((String) this.beanOrName); + Object resolvedBean = this.beanFactory.getBean(this.beanName); // Don't cache non-singletons (e.g., prototypes). if (!this.isSingleton) { return resolvedBean; @@ -237,11 +197,6 @@ public class ControllerAdviceBean implements Ordered { return this.resolvedBean; } - private BeanFactory obtainBeanFactory() { - Assert.state(this.beanFactory != null, "No BeanFactory set"); - return this.beanFactory; - } - /** * Check whether the given bean type should be advised by this * {@code ControllerAdviceBean}. @@ -257,17 +212,17 @@ public class ControllerAdviceBean implements Ordered { @Override public boolean equals(@Nullable Object other) { return (this == other || (other instanceof ControllerAdviceBean that && - this.beanOrName.equals(that.beanOrName) && this.beanFactory == that.beanFactory)); + this.beanName.equals(that.beanName) && this.beanFactory == that.beanFactory)); } @Override public int hashCode() { - return this.beanOrName.hashCode(); + return this.beanName.hashCode(); } @Override public String toString() { - return this.beanOrName.toString(); + return this.beanName; } @@ -308,22 +263,13 @@ public class ControllerAdviceBean implements Ordered { return (beanType != null ? ClassUtils.getUserClass(beanType) : null); } - private static HandlerTypePredicate createBeanTypePredicate(@Nullable Class beanType) { - ControllerAdvice controllerAdvice = (beanType != null ? - AnnotatedElementUtils.findMergedAnnotation(beanType, ControllerAdvice.class) : null); - return createBeanTypePredicate(controllerAdvice); - } - - private static HandlerTypePredicate createBeanTypePredicate(@Nullable ControllerAdvice controllerAdvice) { - if (controllerAdvice != null) { - return HandlerTypePredicate.builder() - .basePackage(controllerAdvice.basePackages()) - .basePackageClass(controllerAdvice.basePackageClasses()) - .assignableType(controllerAdvice.assignableTypes()) - .annotation(controllerAdvice.annotations()) - .build(); - } - return HandlerTypePredicate.forAnyHandlerType(); + private static HandlerTypePredicate createBeanTypePredicate(ControllerAdvice controllerAdvice) { + return HandlerTypePredicate.builder() + .basePackage(controllerAdvice.basePackages()) + .basePackageClass(controllerAdvice.basePackageClasses()) + .assignableType(controllerAdvice.assignableTypes()) + .annotation(controllerAdvice.annotations()) + .build(); } } 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 b04f5c22a96..f89bd5366d5 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 @@ -23,22 +23,23 @@ import java.util.List; import jakarta.annotation.Priority; import org.junit.jupiter.api.Test; -import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.BeanFactory; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.support.StaticApplicationContext; import org.springframework.core.Ordered; import org.springframework.core.PriorityOrdered; +import org.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.core.annotation.Order; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.RestController; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; /** * Unit and integration tests for {@link ControllerAdviceBean}. @@ -48,54 +49,52 @@ import static org.mockito.Mockito.verify; */ class ControllerAdviceBeanTests { + private StaticApplicationContext applicationContext = new StaticApplicationContext(); + @Test - void constructorPreconditions() { + void shouldFailForNullOrEmptyBeanName() { assertThatIllegalArgumentException() - .isThrownBy(() -> new ControllerAdviceBean(null)) - .withMessage("Bean must not be null"); + .isThrownBy(() -> new ControllerAdviceBean(null, null, null)) + .withMessage("Bean name must contain text"); assertThatIllegalArgumentException() - .isThrownBy(() -> new ControllerAdviceBean(null, null)) - .withMessage("Bean name must contain text"); + .isThrownBy(() -> new ControllerAdviceBean(" ", null, null)) + .withMessage("Bean name must contain text"); + } + @Test + void shouldFailForNullBeanFactory() { assertThatIllegalArgumentException() - .isThrownBy(() -> new ControllerAdviceBean("", null)) - .withMessage("Bean name must contain text"); + .isThrownBy(() -> new ControllerAdviceBean("beanName", null, null)) + .withMessage("BeanFactory must not be null"); + } + @Test + void shouldFailWhenBeanFactoryDoesNotContainBean() { + BeanFactory beanFactory = mock(BeanFactory.class); + given(beanFactory.containsBean(eq("beanName"))).willReturn(false); assertThatIllegalArgumentException() - .isThrownBy(() -> new ControllerAdviceBean("\t", null)) - .withMessage("Bean name must contain text"); + .isThrownBy(() -> new ControllerAdviceBean("beanName", beanFactory, null)) + .withMessageContaining("does not contain specified controller advice bean 'beanName'"); + } + @Test + void shouldFailWhenControllerAdviceNull() { + BeanFactory beanFactory = mock(BeanFactory.class); + given(beanFactory.containsBean(eq("beanName"))).willReturn(true); assertThatIllegalArgumentException() - .isThrownBy(() -> new ControllerAdviceBean("myBean", null)) - .withMessage("BeanFactory must not be null"); + .isThrownBy(() -> new ControllerAdviceBean("beanName", beanFactory, null)) + .withMessage("ControllerAdvice must not be null"); } @Test void equalsHashCodeAndToStringForBeanName() { - String beanName = "myBean"; - BeanFactory beanFactory = mock(); - given(beanFactory.containsBean(beanName)).willReturn(true); - - ControllerAdviceBean bean1 = new ControllerAdviceBean(beanName, beanFactory); - ControllerAdviceBean bean2 = new ControllerAdviceBean(beanName, beanFactory); + String beanName = SimpleControllerAdvice.class.getSimpleName(); + ControllerAdviceBean bean1 = createControllerAdviceBean(SimpleControllerAdvice.class); + ControllerAdviceBean bean2 = createControllerAdviceBean(SimpleControllerAdvice.class); assertEqualsHashCodeAndToString(bean1, bean2, beanName); } - @Test - void equalsHashCodeAndToStringForBeanInstance() { - String toString = "beanInstance"; - Object beanInstance = new Object() { - @Override - public String toString() { - return toString; - } - }; - ControllerAdviceBean bean1 = new ControllerAdviceBean(beanInstance); - ControllerAdviceBean bean2 = new ControllerAdviceBean(beanInstance); - assertEqualsHashCodeAndToString(bean1, bean2, toString); - } - @Test void orderedWithLowestPrecedenceByDefaultForBeanName() { assertOrder(SimpleControllerAdvice.class, Ordered.LOWEST_PRECEDENCE); @@ -103,7 +102,7 @@ class ControllerAdviceBeanTests { @Test void orderedWithLowestPrecedenceByDefaultForBeanInstance() { - assertOrder(new SimpleControllerAdvice(), Ordered.LOWEST_PRECEDENCE); + assertOrder(SimpleControllerAdvice.class, Ordered.LOWEST_PRECEDENCE); } @Test @@ -113,7 +112,7 @@ class ControllerAdviceBeanTests { @Test void orderedViaOrderedInterfaceForBeanInstance() { - assertOrder(new OrderedControllerAdvice(), 42); + assertOrder(OrderedControllerAdvice.class, 42); } @Test @@ -124,13 +123,13 @@ class ControllerAdviceBeanTests { @Test void orderedViaAnnotationForBeanInstance() { - assertOrder(new OrderAnnotationControllerAdvice(), 100); - assertOrder(new PriorityAnnotationControllerAdvice(), 200); + assertOrder(OrderAnnotationControllerAdvice.class, 100); + assertOrder(PriorityAnnotationControllerAdvice.class, 200); } @Test void shouldMatchAll() { - ControllerAdviceBean bean = new ControllerAdviceBean(new SimpleControllerAdvice()); + ControllerAdviceBean bean = createControllerAdviceBean(SimpleControllerAdvice.class); assertApplicable("should match all", bean, AnnotatedController.class); assertApplicable("should match all", bean, ImplementationController.class); assertApplicable("should match all", bean, InheritanceController.class); @@ -139,7 +138,7 @@ class ControllerAdviceBeanTests { @Test void basePackageSupport() { - ControllerAdviceBean bean = new ControllerAdviceBean(new BasePackageSupport()); + ControllerAdviceBean bean = createControllerAdviceBean(BasePackageSupport.class); assertApplicable("base package support", bean, AnnotatedController.class); assertApplicable("base package support", bean, ImplementationController.class); assertApplicable("base package support", bean, InheritanceController.class); @@ -148,7 +147,7 @@ class ControllerAdviceBeanTests { @Test void basePackageValueSupport() { - ControllerAdviceBean bean = new ControllerAdviceBean(new BasePackageValueSupport()); + ControllerAdviceBean bean = createControllerAdviceBean(BasePackageValueSupport.class); assertApplicable("base package support", bean, AnnotatedController.class); assertApplicable("base package support", bean, ImplementationController.class); assertApplicable("base package support", bean, InheritanceController.class); @@ -157,14 +156,14 @@ class ControllerAdviceBeanTests { @Test void annotationSupport() { - ControllerAdviceBean bean = new ControllerAdviceBean(new AnnotationSupport()); + ControllerAdviceBean bean = createControllerAdviceBean(AnnotationSupport.class); assertApplicable("annotation support", bean, AnnotatedController.class); assertNotApplicable("this bean is not annotated", bean, InheritanceController.class); } @Test void markerClassSupport() { - ControllerAdviceBean bean = new ControllerAdviceBean(new MarkerClassSupport()); + ControllerAdviceBean bean = createControllerAdviceBean(MarkerClassSupport.class); assertApplicable("base package class support", bean, AnnotatedController.class); assertApplicable("base package class support", bean, ImplementationController.class); assertApplicable("base package class support", bean, InheritanceController.class); @@ -173,7 +172,7 @@ class ControllerAdviceBeanTests { @Test void shouldNotMatch() { - ControllerAdviceBean bean = new ControllerAdviceBean(new ShouldNotMatch()); + ControllerAdviceBean bean = createControllerAdviceBean(ShouldNotMatch.class); assertNotApplicable("should not match", bean, AnnotatedController.class); assertNotApplicable("should not match", bean, ImplementationController.class); assertNotApplicable("should not match", bean, InheritanceController.class); @@ -182,7 +181,7 @@ class ControllerAdviceBeanTests { @Test void assignableTypesSupport() { - ControllerAdviceBean bean = new ControllerAdviceBean(new AssignableTypesSupport()); + ControllerAdviceBean bean = createControllerAdviceBean(AssignableTypesSupport.class); assertApplicable("controller implements assignable", bean, ImplementationController.class); assertApplicable("controller inherits assignable", bean, InheritanceController.class); assertNotApplicable("not assignable", bean, AnnotatedController.class); @@ -191,7 +190,7 @@ class ControllerAdviceBeanTests { @Test void multipleMatch() { - ControllerAdviceBean bean = new ControllerAdviceBean(new MultipleSelectorsSupport()); + ControllerAdviceBean bean = createControllerAdviceBean(MultipleSelectorsSupport.class); assertApplicable("controller implements assignable", bean, ImplementationController.class); assertApplicable("controller is annotated", bean, AnnotatedController.class); assertNotApplicable("should not match", bean, InheritanceController.class); @@ -201,14 +200,14 @@ class ControllerAdviceBeanTests { @SuppressWarnings({"rawtypes", "unchecked"}) public void findAnnotatedBeansSortsBeans() { Class[] expectedTypes = { - // Since ControllerAdviceBean currently treats PriorityOrdered the same as Ordered, - // OrderedControllerAdvice is sorted before PriorityOrderedControllerAdvice. - OrderedControllerAdvice.class, - PriorityOrderedControllerAdvice.class, - OrderAnnotationControllerAdvice.class, - PriorityAnnotationControllerAdvice.class, - SimpleControllerAdviceWithBeanOrder.class, - SimpleControllerAdvice.class, + // Since ControllerAdviceBean currently treats PriorityOrdered the same as Ordered, + // OrderedControllerAdvice is sorted before PriorityOrderedControllerAdvice. + OrderedControllerAdvice.class, + PriorityOrderedControllerAdvice.class, + OrderAnnotationControllerAdvice.class, + PriorityAnnotationControllerAdvice.class, + SimpleControllerAdviceWithBeanOrder.class, + SimpleControllerAdvice.class, }; AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class); @@ -217,6 +216,13 @@ class ControllerAdviceBeanTests { assertThat(adviceBeans).extracting(ControllerAdviceBean::getBeanType).containsExactly(expectedTypes); } + private ControllerAdviceBean createControllerAdviceBean(Class beanType) { + String beanName = beanType.getSimpleName(); + this.applicationContext.registerSingleton(beanName, beanType); + ControllerAdvice controllerAdvice = AnnotatedElementUtils.findMergedAnnotation(beanType, ControllerAdvice.class); + return new ControllerAdviceBean(beanName, this.applicationContext, controllerAdvice); + } + private void assertEqualsHashCodeAndToString(ControllerAdviceBean bean1, ControllerAdviceBean bean2, String toString) { assertThat(bean1).isEqualTo(bean2); assertThat(bean2).isEqualTo(bean1); @@ -225,24 +231,8 @@ class ControllerAdviceBeanTests { assertThat(bean2.toString()).isEqualTo(toString); } - private void assertOrder(Object bean, int expectedOrder) { - assertThat(new ControllerAdviceBean(bean).getOrder()).isEqualTo(expectedOrder); - } - - @SuppressWarnings({"rawtypes", "unchecked"}) - private void assertOrder(Class beanType, int expectedOrder) { - String beanName = "myBean"; - BeanFactory beanFactory = mock(); - 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); - verify(beanFactory).getBean(beanName); + private void assertOrder(Class beanType, int expectedOrder) { + assertThat(createControllerAdviceBean(beanType).getOrder()).isEqualTo(expectedOrder); } private void assertApplicable(String message, ControllerAdviceBean controllerAdvice, Class controllerBeanType) { @@ -259,18 +249,22 @@ class ControllerAdviceBeanTests { // ControllerAdvice classes @ControllerAdvice - static class SimpleControllerAdvice {} + static class SimpleControllerAdvice { + } @ControllerAdvice - static class SimpleControllerAdviceWithBeanOrder {} + static class SimpleControllerAdviceWithBeanOrder { + } @ControllerAdvice @Order(100) - static class OrderAnnotationControllerAdvice {} + static class OrderAnnotationControllerAdvice { + } @ControllerAdvice @Priority(200) - static class PriorityAnnotationControllerAdvice {} + static class PriorityAnnotationControllerAdvice { + } @ControllerAdvice // @Order and @Priority should be ignored due to implementation of Ordered. @@ -297,45 +291,59 @@ class ControllerAdviceBeanTests { } @ControllerAdvice(annotations = ControllerAnnotation.class) - static class AnnotationSupport {} + static class AnnotationSupport { + } @ControllerAdvice(basePackageClasses = MarkerClass.class) - static class MarkerClassSupport {} + static class MarkerClassSupport { + } @ControllerAdvice(assignableTypes = {ControllerInterface.class, AbstractController.class}) - static class AssignableTypesSupport {} + static class AssignableTypesSupport { + } @ControllerAdvice(basePackages = "org.springframework.web.method") - static class BasePackageSupport {} + static class BasePackageSupport { + } @ControllerAdvice("org.springframework.web.method") - static class BasePackageValueSupport {} + static class BasePackageValueSupport { + } @ControllerAdvice(annotations = ControllerAnnotation.class, assignableTypes = ControllerInterface.class) - static class MultipleSelectorsSupport {} + static class MultipleSelectorsSupport { + } @ControllerAdvice(basePackages = "java.util", annotations = {RestController.class}) - static class ShouldNotMatch {} + static class ShouldNotMatch { + } // Support classes - static class MarkerClass {} + static class MarkerClass { + } @Retention(RetentionPolicy.RUNTIME) - @interface ControllerAnnotation {} + @interface ControllerAnnotation { + } @ControllerAnnotation - public static class AnnotatedController {} + public static class AnnotatedController { + } - interface ControllerInterface {} + interface ControllerInterface { + } - static class ImplementationController implements ControllerInterface {} + static class ImplementationController implements ControllerInterface { + } - abstract static class AbstractController {} + abstract static class AbstractController { + } - static class InheritanceController extends AbstractController {} + static class InheritanceController extends AbstractController { + } @Configuration(proxyBeanMethods = false) static class Config { diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/RequestResponseBodyAdviceChainTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/RequestResponseBodyAdviceChainTests.java index 0c95016571a..c2bbb5a5776 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/RequestResponseBodyAdviceChainTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/RequestResponseBodyAdviceChainTests.java @@ -23,7 +23,9 @@ import java.util.List; import org.junit.jupiter.api.Test; +import org.springframework.context.support.StaticApplicationContext; import org.springframework.core.MethodParameter; +import org.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.http.HttpInputMessage; import org.springframework.http.MediaType; import org.springframework.http.converter.HttpMessageConverter; @@ -109,7 +111,7 @@ class RequestResponseBodyAdviceChainTests { @Test void controllerAdvice() { - Object adviceBean = new ControllerAdviceBean(new MyControllerAdvice()); + Object adviceBean = createControllerAdviceBean(MyControllerAdvice.class); RequestResponseBodyAdviceChain chain = new RequestResponseBodyAdviceChain(Collections.singletonList(adviceBean)); String actual = (String) chain.beforeBodyWrite(this.body, this.returnType, this.contentType, @@ -120,7 +122,7 @@ class RequestResponseBodyAdviceChainTests { @Test void controllerAdviceNotApplicable() { - Object adviceBean = new ControllerAdviceBean(new TargetedControllerAdvice()); + Object adviceBean = createControllerAdviceBean(TargetedControllerAdvice.class); RequestResponseBodyAdviceChain chain = new RequestResponseBodyAdviceChain(Collections.singletonList(adviceBean)); String actual = (String) chain.beforeBodyWrite(this.body, this.returnType, this.contentType, @@ -129,6 +131,13 @@ class RequestResponseBodyAdviceChainTests { assertThat(actual).isEqualTo(this.body); } + private ControllerAdviceBean createControllerAdviceBean(Class beanType) { + StaticApplicationContext applicationContext = new StaticApplicationContext(); + applicationContext.registerSingleton(beanType.getSimpleName(), beanType); + ControllerAdvice controllerAdvice = AnnotatedElementUtils.findMergedAnnotation(beanType, ControllerAdvice.class); + return new ControllerAdviceBean(beanType.getSimpleName(), applicationContext, controllerAdvice); + } + @ControllerAdvice private static class MyControllerAdvice implements ResponseBodyAdvice {