Revised NoSuchBeanDefinitionException message and ResolvableType handling
Includes consistent quoting of qualified type names in related classes. Issue: SPR-14831
This commit is contained in:
parent
36332441ae
commit
dc080cb1be
|
|
@ -278,15 +278,15 @@ class TypeConverterDelegate {
|
||||||
|
|
||||||
// Definitely doesn't match: throw IllegalArgumentException/IllegalStateException
|
// Definitely doesn't match: throw IllegalArgumentException/IllegalStateException
|
||||||
StringBuilder msg = new StringBuilder();
|
StringBuilder msg = new StringBuilder();
|
||||||
msg.append("Cannot convert value of type [").append(ClassUtils.getDescriptiveType(newValue));
|
msg.append("Cannot convert value of type '").append(ClassUtils.getDescriptiveType(newValue));
|
||||||
msg.append("] to required type [").append(ClassUtils.getQualifiedName(requiredType)).append("]");
|
msg.append("' to required type '").append(ClassUtils.getQualifiedName(requiredType)).append("'");
|
||||||
if (propertyName != null) {
|
if (propertyName != null) {
|
||||||
msg.append(" for property '").append(propertyName).append("'");
|
msg.append(" for property '").append(propertyName).append("'");
|
||||||
}
|
}
|
||||||
if (editor != null) {
|
if (editor != null) {
|
||||||
msg.append(": PropertyEditor [").append(editor.getClass().getName()).append(
|
msg.append(": PropertyEditor [").append(editor.getClass().getName()).append(
|
||||||
"] returned inappropriate value of type [").append(
|
"] returned inappropriate value of type '").append(
|
||||||
ClassUtils.getDescriptiveType(convertedValue)).append("]");
|
ClassUtils.getDescriptiveType(convertedValue)).append("'");
|
||||||
throw new IllegalArgumentException(msg.toString());
|
throw new IllegalArgumentException(msg.toString());
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2015 the original author or authors.
|
* Copyright 2002-2016 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -57,10 +57,10 @@ public class TypeMismatchException extends PropertyAccessException {
|
||||||
*/
|
*/
|
||||||
public TypeMismatchException(PropertyChangeEvent propertyChangeEvent, Class<?> requiredType, Throwable cause) {
|
public TypeMismatchException(PropertyChangeEvent propertyChangeEvent, Class<?> requiredType, Throwable cause) {
|
||||||
super(propertyChangeEvent,
|
super(propertyChangeEvent,
|
||||||
"Failed to convert property value of type [" +
|
"Failed to convert property value of type '" +
|
||||||
ClassUtils.getDescriptiveType(propertyChangeEvent.getNewValue()) + "]" +
|
ClassUtils.getDescriptiveType(propertyChangeEvent.getNewValue()) + "'" +
|
||||||
(requiredType != null ?
|
(requiredType != null ?
|
||||||
" to required type [" + ClassUtils.getQualifiedName(requiredType) + "]" : "") +
|
" to required type '" + ClassUtils.getQualifiedName(requiredType) + "'" : "") +
|
||||||
(propertyChangeEvent.getPropertyName() != null ?
|
(propertyChangeEvent.getPropertyName() != null ?
|
||||||
" for property '" + propertyChangeEvent.getPropertyName() + "'" : ""),
|
" for property '" + propertyChangeEvent.getPropertyName() + "'" : ""),
|
||||||
cause);
|
cause);
|
||||||
|
|
@ -84,8 +84,8 @@ public class TypeMismatchException extends PropertyAccessException {
|
||||||
* @param cause the root cause (may be {@code null})
|
* @param cause the root cause (may be {@code null})
|
||||||
*/
|
*/
|
||||||
public TypeMismatchException(Object value, Class<?> requiredType, Throwable cause) {
|
public TypeMismatchException(Object value, Class<?> requiredType, Throwable cause) {
|
||||||
super("Failed to convert value of type [" + ClassUtils.getDescriptiveType(value) + "]" +
|
super("Failed to convert value of type '" + ClassUtils.getDescriptiveType(value) + "'" +
|
||||||
(requiredType != null ? " to required type [" + ClassUtils.getQualifiedName(requiredType) + "]" : ""),
|
(requiredType != null ? " to required type '" + ClassUtils.getQualifiedName(requiredType) + "'" : ""),
|
||||||
cause);
|
cause);
|
||||||
this.value = value;
|
this.value = value;
|
||||||
this.requiredType = requiredType;
|
this.requiredType = requiredType;
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
package org.springframework.beans.factory;
|
package org.springframework.beans.factory;
|
||||||
|
|
||||||
import org.springframework.beans.BeansException;
|
import org.springframework.beans.BeansException;
|
||||||
|
import org.springframework.util.ClassUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Thrown when a bean doesn't match the expected type.
|
* Thrown when a bean doesn't match the expected type.
|
||||||
|
|
@ -45,8 +46,8 @@ public class BeanNotOfRequiredTypeException extends BeansException {
|
||||||
* the expected type
|
* the expected type
|
||||||
*/
|
*/
|
||||||
public BeanNotOfRequiredTypeException(String beanName, Class<?> requiredType, Class<?> actualType) {
|
public BeanNotOfRequiredTypeException(String beanName, Class<?> requiredType, Class<?> actualType) {
|
||||||
super("Bean named '" + beanName + "' is expected to be of type [" + requiredType.getName() +
|
super("Bean named '" + beanName + "' is expected to be of type '" + ClassUtils.getQualifiedName(requiredType) +
|
||||||
"] but was actually of type [" + actualType.getName() + "]");
|
"' but was actually of type '" + ClassUtils.getQualifiedName(actualType) + "'");
|
||||||
this.beanName = beanName;
|
this.beanName = beanName;
|
||||||
this.requiredType = requiredType;
|
this.requiredType = requiredType;
|
||||||
this.actualType = actualType;
|
this.actualType = actualType;
|
||||||
|
|
|
||||||
|
|
@ -18,8 +18,6 @@ package org.springframework.beans.factory;
|
||||||
|
|
||||||
import org.springframework.beans.BeansException;
|
import org.springframework.beans.BeansException;
|
||||||
import org.springframework.core.ResolvableType;
|
import org.springframework.core.ResolvableType;
|
||||||
import org.springframework.util.ClassUtils;
|
|
||||||
import org.springframework.util.StringUtils;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exception thrown when a {@code BeanFactory} is asked for a bean instance for which it
|
* Exception thrown when a {@code BeanFactory} is asked for a bean instance for which it
|
||||||
|
|
@ -36,11 +34,9 @@ import org.springframework.util.StringUtils;
|
||||||
@SuppressWarnings("serial")
|
@SuppressWarnings("serial")
|
||||||
public class NoSuchBeanDefinitionException extends BeansException {
|
public class NoSuchBeanDefinitionException extends BeansException {
|
||||||
|
|
||||||
/** Name of the missing bean */
|
|
||||||
private String beanName;
|
private String beanName;
|
||||||
|
|
||||||
/** Required type of the missing bean */
|
private ResolvableType resolvableType;
|
||||||
private ResolvableType beanResolvableType;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -48,7 +44,7 @@ public class NoSuchBeanDefinitionException extends BeansException {
|
||||||
* @param name the name of the missing bean
|
* @param name the name of the missing bean
|
||||||
*/
|
*/
|
||||||
public NoSuchBeanDefinitionException(String name) {
|
public NoSuchBeanDefinitionException(String name) {
|
||||||
super("No bean named '" + name + "' is defined");
|
super("No bean named '" + name + "' available");
|
||||||
this.beanName = name;
|
this.beanName = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -58,7 +54,7 @@ public class NoSuchBeanDefinitionException extends BeansException {
|
||||||
* @param message detailed message describing the problem
|
* @param message detailed message describing the problem
|
||||||
*/
|
*/
|
||||||
public NoSuchBeanDefinitionException(String name, String message) {
|
public NoSuchBeanDefinitionException(String name, String message) {
|
||||||
super("No bean named '" + name + "' is defined: " + message);
|
super("No bean named '" + name + "' available: " + message);
|
||||||
this.beanName = name;
|
this.beanName = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -67,8 +63,7 @@ public class NoSuchBeanDefinitionException extends BeansException {
|
||||||
* @param type required type of the missing bean
|
* @param type required type of the missing bean
|
||||||
*/
|
*/
|
||||||
public NoSuchBeanDefinitionException(Class<?> type) {
|
public NoSuchBeanDefinitionException(Class<?> type) {
|
||||||
super("No qualifying bean of type [" + type.getName() + "] is defined");
|
this(ResolvableType.forClass(type));
|
||||||
this.beanResolvableType = ResolvableType.forClass(type);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -77,43 +72,28 @@ public class NoSuchBeanDefinitionException extends BeansException {
|
||||||
* @param message detailed message describing the problem
|
* @param message detailed message describing the problem
|
||||||
*/
|
*/
|
||||||
public NoSuchBeanDefinitionException(Class<?> type, String message) {
|
public NoSuchBeanDefinitionException(Class<?> type, String message) {
|
||||||
super("No qualifying bean of type [" + ClassUtils.getQualifiedName(type) + "] is defined: " + message);
|
this(ResolvableType.forClass(type), message);
|
||||||
this.beanResolvableType = ResolvableType.forClass(type);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new {@code NoSuchBeanDefinitionException}.
|
* Create a new {@code NoSuchBeanDefinitionException}.
|
||||||
* @param type required type of the missing bean
|
* @param type full type declaration of the missing bean
|
||||||
* @param dependencyDescription a description of the originating dependency
|
* @since 4.3.4
|
||||||
* @param message detailed message describing the problem
|
|
||||||
* @deprecated as of Spring 5.0, in favor of {@link #NoSuchBeanDefinitionException(ResolvableType, String)}
|
|
||||||
*/
|
*/
|
||||||
@Deprecated
|
public NoSuchBeanDefinitionException(ResolvableType type) {
|
||||||
public NoSuchBeanDefinitionException(Class<?> type, String dependencyDescription, String message) {
|
super("No qualifying bean of type '" + type + "' available");
|
||||||
this(ResolvableType.forClass(type), dependencyDescription, message);
|
this.resolvableType = type;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new {@code NoSuchBeanDefinitionException}.
|
* Create a new {@code NoSuchBeanDefinitionException}.
|
||||||
* @param resolvableType required type of the missing bean
|
* @param type full type declaration of the missing bean
|
||||||
* @param message detailed message describing the problem
|
* @param message detailed message describing the problem
|
||||||
|
* @since 4.3.4
|
||||||
*/
|
*/
|
||||||
public NoSuchBeanDefinitionException(ResolvableType resolvableType, String message) {
|
public NoSuchBeanDefinitionException(ResolvableType type, String message) {
|
||||||
this(resolvableType, resolvableType.toString(), message);
|
super("No qualifying bean of type '" + type + "' available: " + message);
|
||||||
}
|
this.resolvableType = type;
|
||||||
|
|
||||||
/**
|
|
||||||
* 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(resolvableType.getRawClass()) + "]" : "") +
|
|
||||||
" found for dependency" + (StringUtils.hasLength(dependencyDescription) ? " [" +
|
|
||||||
dependencyDescription + "]" : "") + ": " + message);
|
|
||||||
this.beanResolvableType = resolvableType;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -125,18 +105,20 @@ public class NoSuchBeanDefinitionException extends BeansException {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the required {@link ResolvableType} of the missing bean, if it was a lookup
|
* Return the required type of the missing bean, if it was a lookup <em>by type</em>
|
||||||
* <em>by type</em> that failed.
|
* that failed.
|
||||||
*/
|
*/
|
||||||
public ResolvableType getBeanResolvableType() {
|
public Class<?> getBeanType() {
|
||||||
return this.beanResolvableType;
|
return (this.resolvableType != null ? this.resolvableType.getRawClass() : null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the required type of the missing bean, if it was a lookup <em>by type</em> that failed.
|
* Return the required {@link ResolvableType} of the missing bean, if it was a lookup
|
||||||
|
* <em>by type</em> that failed.
|
||||||
|
* @since 4.3.4
|
||||||
*/
|
*/
|
||||||
public Class<?> getBeanType() {
|
public ResolvableType getResolvableType() {
|
||||||
return (this.beanResolvableType != null ? this.beanResolvableType.getRawClass() : null);
|
return this.resolvableType;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -82,7 +82,7 @@ public class ContextAnnotationAutowireCandidateResolver extends QualifierAnnotat
|
||||||
public Object getTarget() {
|
public Object getTarget() {
|
||||||
Object target = beanFactory.doResolveDependency(descriptor, beanName, null, null);
|
Object target = beanFactory.doResolveDependency(descriptor, beanName, null, null);
|
||||||
if (target == null) {
|
if (target == null) {
|
||||||
throw new NoSuchBeanDefinitionException(descriptor.getDependencyType(),
|
throw new NoSuchBeanDefinitionException(descriptor.getResolvableType(),
|
||||||
"Optional dependency not present for lazy injection point");
|
"Optional dependency not present for lazy injection point");
|
||||||
}
|
}
|
||||||
return target;
|
return target;
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,7 @@ class GroovyApplicationContextDynamicBeanPropertyTests {
|
||||||
|
|
||||||
def err = shouldFail NoSuchBeanDefinitionException, { ctx.someNonExistentBean }
|
def err = shouldFail NoSuchBeanDefinitionException, { ctx.someNonExistentBean }
|
||||||
|
|
||||||
assertEquals "No bean named 'someNonExistentBean' is defined", err.message
|
assertEquals "No bean named 'someNonExistentBean' available", err.message
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -128,7 +128,7 @@ public class AnnotationConfigApplicationContextTests {
|
||||||
}
|
}
|
||||||
catch (NoSuchBeanDefinitionException ex) {
|
catch (NoSuchBeanDefinitionException ex) {
|
||||||
assertThat(ex.getMessage(), containsString(
|
assertThat(ex.getMessage(), containsString(
|
||||||
format("No qualifying bean of type [%s] is defined", targetType.getName())));
|
format("No qualifying bean of type '%s'", targetType.getName())));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -142,7 +142,7 @@ public class AnnotationConfigApplicationContextTests {
|
||||||
catch (NoSuchBeanDefinitionException ex) {
|
catch (NoSuchBeanDefinitionException ex) {
|
||||||
assertThat(ex.getMessage(),
|
assertThat(ex.getMessage(),
|
||||||
allOf(
|
allOf(
|
||||||
containsString("No qualifying bean of type [" + TestBean.class.getName() + "] is defined"),
|
containsString("No qualifying bean of type '" + TestBean.class.getName() + "'"),
|
||||||
containsString("tb1"),
|
containsString("tb1"),
|
||||||
containsString("tb2")
|
containsString("tb2")
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -291,7 +291,7 @@ public class BeanPropertyRowMapper<T> implements RowMapper<T> {
|
||||||
Object value = getColumnValue(rs, index, pd);
|
Object value = getColumnValue(rs, index, pd);
|
||||||
if (rowNumber == 0 && logger.isDebugEnabled()) {
|
if (rowNumber == 0 && logger.isDebugEnabled()) {
|
||||||
logger.debug("Mapping column '" + column + "' to property '" + pd.getName() +
|
logger.debug("Mapping column '" + column + "' to property '" + pd.getName() +
|
||||||
"' of type [" + ClassUtils.getQualifiedName(pd.getPropertyType()) + "]");
|
"' of type '" + ClassUtils.getQualifiedName(pd.getPropertyType()) + "'");
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
bw.setPropertyValue(pd.getName(), value);
|
bw.setPropertyValue(pd.getName(), value);
|
||||||
|
|
@ -301,9 +301,9 @@ public class BeanPropertyRowMapper<T> implements RowMapper<T> {
|
||||||
if (logger.isDebugEnabled()) {
|
if (logger.isDebugEnabled()) {
|
||||||
logger.debug("Intercepted TypeMismatchException for row " + rowNumber +
|
logger.debug("Intercepted TypeMismatchException for row " + rowNumber +
|
||||||
" and column '" + column + "' with null value when setting property '" +
|
" and column '" + column + "' with null value when setting property '" +
|
||||||
pd.getName() + "' of type [" +
|
pd.getName() + "' of type '" +
|
||||||
ClassUtils.getQualifiedName(pd.getPropertyType()) +
|
ClassUtils.getQualifiedName(pd.getPropertyType()) +
|
||||||
"] on object: " + mappedObject, ex);
|
"' on object: " + mappedObject, ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|
|
||||||
|
|
@ -73,10 +73,9 @@ public class MessageMethodArgumentResolver implements HandlerMethodArgumentResol
|
||||||
Class<?> targetPayloadType = getPayloadType(parameter);
|
Class<?> targetPayloadType = getPayloadType(parameter);
|
||||||
|
|
||||||
if (!targetMessageType.isAssignableFrom(message.getClass())) {
|
if (!targetMessageType.isAssignableFrom(message.getClass())) {
|
||||||
String actual = ClassUtils.getQualifiedName(message.getClass());
|
throw new MethodArgumentTypeMismatchException(message, parameter, "Actual message type '" +
|
||||||
String expected = ClassUtils.getQualifiedName(targetMessageType);
|
ClassUtils.getDescriptiveType(message) + "' does not match expected type '" +
|
||||||
throw new MethodArgumentTypeMismatchException(message, parameter, "The actual message type " +
|
ClassUtils.getQualifiedName(targetMessageType) + "'");
|
||||||
"[" + actual + "] does not match the expected type [" + expected + "]");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Object payload = message.getPayload();
|
Object payload = message.getPayload();
|
||||||
|
|
@ -85,11 +84,9 @@ public class MessageMethodArgumentResolver implements HandlerMethodArgumentResol
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isEmptyPayload(payload)) {
|
if (isEmptyPayload(payload)) {
|
||||||
String actual = ClassUtils.getQualifiedName(payload.getClass());
|
throw new MessageConversionException(message, "Cannot convert from actual payload type '" +
|
||||||
String expected = ClassUtils.getQualifiedName(targetPayloadType);
|
ClassUtils.getDescriptiveType(payload) + "' to expected payload type '" +
|
||||||
throw new MessageConversionException(message, "Cannot convert from the " +
|
ClassUtils.getQualifiedName(targetPayloadType) + "' when payload is empty");
|
||||||
"expected payload type [" + expected + "] to the " +
|
|
||||||
"actual payload type [" + actual + "] when the payload is empty.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
payload = convertPayload(message, parameter, targetPayloadType);
|
payload = convertPayload(message, parameter, targetPayloadType);
|
||||||
|
|
@ -132,10 +129,9 @@ public class MessageMethodArgumentResolver implements HandlerMethodArgumentResol
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result == null) {
|
if (result == null) {
|
||||||
String actual = ClassUtils.getQualifiedName(targetPayloadType);
|
throw new MessageConversionException(message, "No converter found from actual payload type '" +
|
||||||
String expected = ClassUtils.getQualifiedName(message.getPayload().getClass());
|
ClassUtils.getDescriptiveType(message.getPayload()) + "' to expected payload type '" +
|
||||||
throw new MessageConversionException(message, "No converter found to convert payload type [" +
|
ClassUtils.getQualifiedName(targetPayloadType) + "'");
|
||||||
actual + "] to expected payload type [" + expected + "]");
|
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -124,7 +124,7 @@ public class MessageMethodArgumentResolverTests {
|
||||||
|
|
||||||
assertTrue(this.resolver.supportsParameter(parameter));
|
assertTrue(this.resolver.supportsParameter(parameter));
|
||||||
thrown.expect(MessageConversionException.class);
|
thrown.expect(MessageConversionException.class);
|
||||||
thrown.expectMessage("the payload is empty");
|
thrown.expectMessage("payload is empty");
|
||||||
thrown.expectMessage(Integer.class.getName());
|
thrown.expectMessage(Integer.class.getName());
|
||||||
thrown.expectMessage(String.class.getName());
|
thrown.expectMessage(String.class.getName());
|
||||||
this.resolver.resolveArgument(parameter, message);
|
this.resolver.resolveArgument(parameter, message);
|
||||||
|
|
@ -216,7 +216,7 @@ public class MessageMethodArgumentResolverTests {
|
||||||
|
|
||||||
assertTrue(this.resolver.supportsParameter(parameter));
|
assertTrue(this.resolver.supportsParameter(parameter));
|
||||||
thrown.expect(MessageConversionException.class);
|
thrown.expect(MessageConversionException.class);
|
||||||
thrown.expectMessage("the payload is empty");
|
thrown.expectMessage("payload is empty");
|
||||||
thrown.expectMessage(Integer.class.getName());
|
thrown.expectMessage(Integer.class.getName());
|
||||||
thrown.expectMessage(String.class.getName());
|
thrown.expectMessage(String.class.getName());
|
||||||
this.resolver.resolveArgument(parameter, message);
|
this.resolver.resolveArgument(parameter, message);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue