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:
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user