Document ControllerAdviceBean as internal usage
This commit documents `ControllerAdviceBean` as internal usage, as it is not meant for application to manually create controller advice bean instances. This also refactors the existing partial implementation of the support for creating controller advice beans "programmatically". Closes gh-32776
This commit is contained in:
parent
94e2bef9a3
commit
b888f362e5
|
|
@ -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.
|
||||
*
|
||||
* <p>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.
|
||||
* <p>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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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<String> {
|
||||
|
|
|
|||
Loading…
Reference in New Issue