Expose ResolvableType in NoSuchBeanDefinitionException

This commit improves NoSuchBeanDefinitionException to expose a full
ResolvableType rather than a raw class if a lookup by type failed. This
allows to know more about the underlying type and is typically useful
when a collection or map is required as the relevant generic type is the
actual bean that wasn't found.

Issue: SPR-14831
This commit is contained in:
Stephane Nicoll
2016-10-21 15:02:27 +02:00
parent efe3996cf9
commit b7d85f8ebc
2 changed files with 42 additions and 11 deletions

View File

@@ -17,6 +17,7 @@
package org.springframework.beans.factory;
import org.springframework.beans.BeansException;
import org.springframework.core.ResolvableType;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
@@ -27,6 +28,7 @@ import org.springframework.util.StringUtils;
*
* @author Rod Johnson
* @author Juergen Hoeller
* @author Stephane Nicoll
* @see BeanFactory#getBean(String)
* @see BeanFactory#getBean(Class)
* @see NoUniqueBeanDefinitionException
@@ -38,7 +40,7 @@ public class NoSuchBeanDefinitionException extends BeansException {
private String beanName;
/** Required type of the missing bean */
private Class<?> beanType;
private ResolvableType beanResolvableType;
/**
@@ -66,7 +68,7 @@ public class NoSuchBeanDefinitionException extends BeansException {
*/
public NoSuchBeanDefinitionException(Class<?> type) {
super("No qualifying bean of type [" + type.getName() + "] is defined");
this.beanType = type;
this.beanResolvableType = ResolvableType.forClass(type);
}
/**
@@ -76,7 +78,7 @@ public class NoSuchBeanDefinitionException extends BeansException {
*/
public NoSuchBeanDefinitionException(Class<?> type, String message) {
super("No qualifying bean of type [" + ClassUtils.getQualifiedName(type) + "] is defined: " + message);
this.beanType = type;
this.beanResolvableType = ResolvableType.forClass(type);
}
/**
@@ -84,13 +86,34 @@ public class NoSuchBeanDefinitionException extends BeansException {
* @param type required type of the missing bean
* @param dependencyDescription a description of the originating dependency
* @param message detailed message describing the problem
* @deprecated as of Spring 5.0, in favor of {@link #NoSuchBeanDefinitionException(ResolvableType, String)}
*/
@Deprecated
public NoSuchBeanDefinitionException(Class<?> type, String dependencyDescription, String message) {
this(ResolvableType.forClass(type), dependencyDescription, message);
}
/**
* Create a new {@code NoSuchBeanDefinitionException}.
* @param resolvableType required type of the missing bean
* @param message detailed message describing the problem
*/
public NoSuchBeanDefinitionException(ResolvableType resolvableType, String message) {
this(resolvableType, resolvableType.toString(), message);
}
/**
* Create a new {@code NoSuchBeanDefinitionException}.
* @param resolvableType required type of the missing bean
* @param dependencyDescription a description of the originating dependency
* @param message detailed message describing the problem
*/
private NoSuchBeanDefinitionException(ResolvableType resolvableType, String dependencyDescription, String message) {
super("No qualifying bean" + (!StringUtils.hasLength(dependencyDescription) ?
" of type [" + ClassUtils.getQualifiedName(type) + "]" : "") + " found for dependency" +
(StringUtils.hasLength(dependencyDescription) ? " [" + dependencyDescription + "]" : "") +
": " + message);
this.beanType = type;
" of type [" + ClassUtils.getQualifiedName(resolvableType.getRawClass()) + "]" : "") +
" found for dependency" + (StringUtils.hasLength(dependencyDescription) ? " [" +
dependencyDescription + "]" : "") + ": " + message);
this.beanResolvableType = resolvableType;
}
@@ -101,11 +124,19 @@ public class NoSuchBeanDefinitionException extends BeansException {
return this.beanName;
}
/**
* Return the required {@link ResolvableType} of the missing bean, if it was a lookup
* <em>by type</em> that failed.
*/
public ResolvableType getBeanResolvableType() {
return this.beanResolvableType;
}
/**
* Return the required type of the missing bean, if it was a lookup <em>by type</em> that failed.
*/
public Class<?> getBeanType() {
return this.beanType;
return this.beanResolvableType.getRawClass();
}
/**

View File

@@ -1081,7 +1081,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);
if (matchingBeans.isEmpty()) {
if (descriptor.isRequired()) {
raiseNoMatchingBeanFound(type, descriptor.getResolvableType().toString(), descriptor);
raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
}
return null;
}
@@ -1444,11 +1444,11 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
* for an unresolvable dependency.
*/
private void raiseNoMatchingBeanFound(
Class<?> type, String dependencyDescription, DependencyDescriptor descriptor) throws BeansException {
Class<?> type, ResolvableType resolvableType, DependencyDescriptor descriptor) throws BeansException {
checkBeanNotOfRequiredType(type, descriptor);
throw new NoSuchBeanDefinitionException(type, dependencyDescription,
throw new NoSuchBeanDefinitionException(resolvableType,
"expected at least 1 bean which qualifies as autowire candidate. " +
"Dependency annotations: " + ObjectUtils.nullSafeToString(descriptor.getAnnotations()));
}