Skip to content

Commit 0ee8322

Browse files
committed
Revised NoSuchBeanDefinitionException message and ResolvableType handling
Includes consistent quoting of qualified type names in related classes. Issue: SPR-14831 (cherry picked from commit dc080cb)
1 parent 997fb5f commit 0ee8322

File tree

10 files changed

+76
-47
lines changed

10 files changed

+76
-47
lines changed

spring-beans/src/main/java/org/springframework/beans/TypeConverterDelegate.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -290,15 +290,15 @@ else if (conversionService != null) {
290290

291291
// Definitely doesn't match: throw IllegalArgumentException/IllegalStateException
292292
StringBuilder msg = new StringBuilder();
293-
msg.append("Cannot convert value of type [").append(ClassUtils.getDescriptiveType(newValue));
294-
msg.append("] to required type [").append(ClassUtils.getQualifiedName(requiredType)).append("]");
293+
msg.append("Cannot convert value of type '").append(ClassUtils.getDescriptiveType(newValue));
294+
msg.append("' to required type '").append(ClassUtils.getQualifiedName(requiredType)).append("'");
295295
if (propertyName != null) {
296296
msg.append(" for property '").append(propertyName).append("'");
297297
}
298298
if (editor != null) {
299299
msg.append(": PropertyEditor [").append(editor.getClass().getName()).append(
300-
"] returned inappropriate value of type [").append(
301-
ClassUtils.getDescriptiveType(convertedValue)).append("]");
300+
"] returned inappropriate value of type '").append(
301+
ClassUtils.getDescriptiveType(convertedValue)).append("'");
302302
throw new IllegalArgumentException(msg.toString());
303303
}
304304
else {

spring-beans/src/main/java/org/springframework/beans/TypeMismatchException.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2015 the original author or authors.
2+
* Copyright 2002-2016 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -57,10 +57,10 @@ public TypeMismatchException(PropertyChangeEvent propertyChangeEvent, Class<?> r
5757
*/
5858
public TypeMismatchException(PropertyChangeEvent propertyChangeEvent, Class<?> requiredType, Throwable cause) {
5959
super(propertyChangeEvent,
60-
"Failed to convert property value of type [" +
61-
ClassUtils.getDescriptiveType(propertyChangeEvent.getNewValue()) + "]" +
60+
"Failed to convert property value of type '" +
61+
ClassUtils.getDescriptiveType(propertyChangeEvent.getNewValue()) + "'" +
6262
(requiredType != null ?
63-
" to required type [" + ClassUtils.getQualifiedName(requiredType) + "]" : "") +
63+
" to required type '" + ClassUtils.getQualifiedName(requiredType) + "'" : "") +
6464
(propertyChangeEvent.getPropertyName() != null ?
6565
" for property '" + propertyChangeEvent.getPropertyName() + "'" : ""),
6666
cause);
@@ -84,8 +84,8 @@ public TypeMismatchException(Object value, Class<?> requiredType) {
8484
* @param cause the root cause (may be {@code null})
8585
*/
8686
public TypeMismatchException(Object value, Class<?> requiredType, Throwable cause) {
87-
super("Failed to convert value of type [" + ClassUtils.getDescriptiveType(value) + "]" +
88-
(requiredType != null ? " to required type [" + ClassUtils.getQualifiedName(requiredType) + "]" : ""),
87+
super("Failed to convert value of type '" + ClassUtils.getDescriptiveType(value) + "'" +
88+
(requiredType != null ? " to required type '" + ClassUtils.getQualifiedName(requiredType) + "'" : ""),
8989
cause);
9090
this.value = value;
9191
this.requiredType = requiredType;

spring-beans/src/main/java/org/springframework/beans/factory/BeanNotOfRequiredTypeException.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package org.springframework.beans.factory;
1818

1919
import org.springframework.beans.BeansException;
20+
import org.springframework.util.ClassUtils;
2021

2122
/**
2223
* Thrown when a bean doesn't match the expected type.
@@ -45,8 +46,8 @@ public class BeanNotOfRequiredTypeException extends BeansException {
4546
* the expected type
4647
*/
4748
public BeanNotOfRequiredTypeException(String beanName, Class<?> requiredType, Class<?> actualType) {
48-
super("Bean named '" + beanName + "' is expected to be of type [" + requiredType.getName() +
49-
"] but was actually of type [" + actualType.getName() + "]");
49+
super("Bean named '" + beanName + "' is expected to be of type '" + ClassUtils.getQualifiedName(requiredType) +
50+
"' but was actually of type '" + ClassUtils.getQualifiedName(actualType) + "'");
5051
this.beanName = beanName;
5152
this.requiredType = requiredType;
5253
this.actualType = actualType;

spring-beans/src/main/java/org/springframework/beans/factory/NoSuchBeanDefinitionException.java

Lines changed: 44 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package org.springframework.beans.factory;
1818

1919
import org.springframework.beans.BeansException;
20+
import org.springframework.core.ResolvableType;
2021
import org.springframework.util.ClassUtils;
2122
import org.springframework.util.StringUtils;
2223

@@ -27,26 +28,25 @@
2728
*
2829
* @author Rod Johnson
2930
* @author Juergen Hoeller
31+
* @author Stephane Nicoll
3032
* @see BeanFactory#getBean(String)
3133
* @see BeanFactory#getBean(Class)
3234
* @see NoUniqueBeanDefinitionException
3335
*/
3436
@SuppressWarnings("serial")
3537
public class NoSuchBeanDefinitionException extends BeansException {
3638

37-
/** Name of the missing bean */
3839
private String beanName;
3940

40-
/** Required type of the missing bean */
41-
private Class<?> beanType;
41+
private ResolvableType resolvableType;
4242

4343

4444
/**
4545
* Create a new {@code NoSuchBeanDefinitionException}.
4646
* @param name the name of the missing bean
4747
*/
4848
public NoSuchBeanDefinitionException(String name) {
49-
super("No bean named '" + name + "' is defined");
49+
super("No bean named '" + name + "' available");
5050
this.beanName = name;
5151
}
5252

@@ -56,7 +56,7 @@ public NoSuchBeanDefinitionException(String name) {
5656
* @param message detailed message describing the problem
5757
*/
5858
public NoSuchBeanDefinitionException(String name, String message) {
59-
super("No bean named '" + name + "' is defined: " + message);
59+
super("No bean named '" + name + "' available: " + message);
6060
this.beanName = name;
6161
}
6262

@@ -65,8 +65,7 @@ public NoSuchBeanDefinitionException(String name, String message) {
6565
* @param type required type of the missing bean
6666
*/
6767
public NoSuchBeanDefinitionException(Class<?> type) {
68-
super("No qualifying bean of type [" + type.getName() + "] is defined");
69-
this.beanType = type;
68+
this(ResolvableType.forClass(type));
7069
}
7170

7271
/**
@@ -75,22 +74,44 @@ public NoSuchBeanDefinitionException(Class<?> type) {
7574
* @param message detailed message describing the problem
7675
*/
7776
public NoSuchBeanDefinitionException(Class<?> type, String message) {
78-
super("No qualifying bean of type [" + ClassUtils.getQualifiedName(type) + "] is defined: " + message);
79-
this.beanType = type;
77+
this(ResolvableType.forClass(type), message);
78+
}
79+
80+
/**
81+
* Create a new {@code NoSuchBeanDefinitionException}.
82+
* @param type full type declaration of the missing bean
83+
* @since 4.3.4
84+
*/
85+
public NoSuchBeanDefinitionException(ResolvableType type) {
86+
super("No qualifying bean of type '" + type + "' available");
87+
this.resolvableType = type;
88+
}
89+
90+
/**
91+
* Create a new {@code NoSuchBeanDefinitionException}.
92+
* @param type full type declaration of the missing bean
93+
* @param message detailed message describing the problem
94+
* @since 4.3.4
95+
*/
96+
public NoSuchBeanDefinitionException(ResolvableType type, String message) {
97+
super("No qualifying bean of type '" + type + "' available: " + message);
98+
this.resolvableType = type;
8099
}
81100

82101
/**
83102
* Create a new {@code NoSuchBeanDefinitionException}.
84103
* @param type required type of the missing bean
85104
* @param dependencyDescription a description of the originating dependency
86105
* @param message detailed message describing the problem
106+
* @deprecated as of 4.3.4, in favor of {@link #NoSuchBeanDefinitionException(ResolvableType, String)}
87107
*/
108+
@Deprecated
88109
public NoSuchBeanDefinitionException(Class<?> type, String dependencyDescription, String message) {
89110
super("No qualifying bean" + (!StringUtils.hasLength(dependencyDescription) ?
90-
" of type [" + ClassUtils.getQualifiedName(type) + "]" : "") + " found for dependency" +
111+
" of type '" + ClassUtils.getQualifiedName(type) + "'" : "") + " found for dependency" +
91112
(StringUtils.hasLength(dependencyDescription) ? " [" + dependencyDescription + "]" : "") +
92113
": " + message);
93-
this.beanType = type;
114+
this.resolvableType = ResolvableType.forClass(type);
94115
}
95116

96117

@@ -102,10 +123,20 @@ public String getBeanName() {
102123
}
103124

104125
/**
105-
* Return the required type of the missing bean, if it was a lookup <em>by type</em> that failed.
126+
* Return the required type of the missing bean, if it was a lookup <em>by type</em>
127+
* that failed.
106128
*/
107129
public Class<?> getBeanType() {
108-
return this.beanType;
130+
return (this.resolvableType != null ? this.resolvableType.getRawClass() : null);
131+
}
132+
133+
/**
134+
* Return the required {@link ResolvableType} of the missing bean, if it was a lookup
135+
* <em>by type</em> that failed.
136+
* @since 4.3.4
137+
*/
138+
public ResolvableType getResolvableType() {
139+
return this.resolvableType;
109140
}
110141

111142
/**

spring-context/src/main/java/org/springframework/context/annotation/ContextAnnotationAutowireCandidateResolver.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ public boolean isStatic() {
8282
public Object getTarget() {
8383
Object target = beanFactory.doResolveDependency(descriptor, beanName, null, null);
8484
if (target == null) {
85-
throw new NoSuchBeanDefinitionException(descriptor.getDependencyType(),
85+
throw new NoSuchBeanDefinitionException(descriptor.getResolvableType(),
8686
"Optional dependency not present for lazy injection point");
8787
}
8888
return target;

spring-context/src/test/groovy/org/springframework/context/groovy/GroovyApplicationContextDynamicBeanPropertyTests.groovy

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ class GroovyApplicationContextDynamicBeanPropertyTests {
4848

4949
def err = shouldFail NoSuchBeanDefinitionException, { ctx.someNonExistentBean }
5050

51-
assertEquals "No bean named 'someNonExistentBean' is defined", err.message
51+
assertEquals "No bean named 'someNonExistentBean' available", err.message
5252
}
53+
5354
}

spring-context/src/test/java/org/springframework/context/annotation/AnnotationConfigApplicationContextTests.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ public void getBeanByTypeRaisesNoSuchBeanDefinitionException() {
128128
}
129129
catch (NoSuchBeanDefinitionException ex) {
130130
assertThat(ex.getMessage(), containsString(
131-
format("No qualifying bean of type [%s] is defined", targetType.getName())));
131+
format("No qualifying bean of type '%s'", targetType.getName())));
132132
}
133133
}
134134

@@ -142,7 +142,7 @@ public void getBeanByTypeAmbiguityRaisesException() {
142142
catch (NoSuchBeanDefinitionException ex) {
143143
assertThat(ex.getMessage(),
144144
allOf(
145-
containsString("No qualifying bean of type [" + TestBean.class.getName() + "] is defined"),
145+
containsString("No qualifying bean of type '" + TestBean.class.getName() + "'"),
146146
containsString("tb1"),
147147
containsString("tb2")
148148
)

spring-jdbc/src/main/java/org/springframework/jdbc/core/BeanPropertyRowMapper.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -291,7 +291,7 @@ public T mapRow(ResultSet rs, int rowNumber) throws SQLException {
291291
Object value = getColumnValue(rs, index, pd);
292292
if (rowNumber == 0 && logger.isDebugEnabled()) {
293293
logger.debug("Mapping column '" + column + "' to property '" + pd.getName() +
294-
"' of type [" + ClassUtils.getQualifiedName(pd.getPropertyType()) + "]");
294+
"' of type '" + ClassUtils.getQualifiedName(pd.getPropertyType()) + "'");
295295
}
296296
try {
297297
bw.setPropertyValue(pd.getName(), value);
@@ -301,9 +301,9 @@ public T mapRow(ResultSet rs, int rowNumber) throws SQLException {
301301
if (logger.isDebugEnabled()) {
302302
logger.debug("Intercepted TypeMismatchException for row " + rowNumber +
303303
" and column '" + column + "' with null value when setting property '" +
304-
pd.getName() + "' of type [" +
304+
pd.getName() + "' of type '" +
305305
ClassUtils.getQualifiedName(pd.getPropertyType()) +
306-
"] on object: " + mappedObject, ex);
306+
"' on object: " + mappedObject, ex);
307307
}
308308
}
309309
else {

spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/support/MessageMethodArgumentResolver.java

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -73,10 +73,9 @@ public Object resolveArgument(MethodParameter parameter, Message<?> message) thr
7373
Class<?> targetPayloadType = getPayloadType(parameter);
7474

7575
if (!targetMessageType.isAssignableFrom(message.getClass())) {
76-
String actual = ClassUtils.getQualifiedName(message.getClass());
77-
String expected = ClassUtils.getQualifiedName(targetMessageType);
78-
throw new MethodArgumentTypeMismatchException(message, parameter, "The actual message type " +
79-
"[" + actual + "] does not match the expected type [" + expected + "]");
76+
throw new MethodArgumentTypeMismatchException(message, parameter, "Actual message type '" +
77+
ClassUtils.getDescriptiveType(message) + "' does not match expected type '" +
78+
ClassUtils.getQualifiedName(targetMessageType) + "'");
8079
}
8180

8281
Object payload = message.getPayload();
@@ -85,11 +84,9 @@ public Object resolveArgument(MethodParameter parameter, Message<?> message) thr
8584
}
8685

8786
if (isEmptyPayload(payload)) {
88-
String actual = ClassUtils.getQualifiedName(payload.getClass());
89-
String expected = ClassUtils.getQualifiedName(targetPayloadType);
90-
throw new MessageConversionException(message, "Cannot convert from the " +
91-
"expected payload type [" + expected + "] to the " +
92-
"actual payload type [" + actual + "] when the payload is empty.");
87+
throw new MessageConversionException(message, "Cannot convert from actual payload type '" +
88+
ClassUtils.getDescriptiveType(payload) + "' to expected payload type '" +
89+
ClassUtils.getQualifiedName(targetPayloadType) + "' when payload is empty");
9390
}
9491

9592
payload = convertPayload(message, parameter, targetPayloadType);
@@ -132,10 +129,9 @@ else if (this.converter != null) {
132129
}
133130

134131
if (result == null) {
135-
String actual = ClassUtils.getQualifiedName(targetPayloadType);
136-
String expected = ClassUtils.getQualifiedName(message.getPayload().getClass());
137-
throw new MessageConversionException(message, "No converter found to convert payload type [" +
138-
actual + "] to expected payload type [" + expected + "]");
132+
throw new MessageConversionException(message, "No converter found from actual payload type '" +
133+
ClassUtils.getDescriptiveType(message.getPayload()) + "' to expected payload type '" +
134+
ClassUtils.getQualifiedName(targetPayloadType) + "'");
139135
}
140136
return result;
141137
}

spring-messaging/src/test/java/org/springframework/messaging/handler/annotation/support/MessageMethodArgumentResolverTests.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ public void resolveWithConversionEmptyPayload() throws Exception {
124124

125125
assertTrue(this.resolver.supportsParameter(parameter));
126126
thrown.expect(MessageConversionException.class);
127-
thrown.expectMessage("the payload is empty");
127+
thrown.expectMessage("payload is empty");
128128
thrown.expectMessage(Integer.class.getName());
129129
thrown.expectMessage(String.class.getName());
130130
this.resolver.resolveArgument(parameter, message);
@@ -216,7 +216,7 @@ public void resolveWithConversionEmptyPayloadButNoConverter() throws Exception {
216216

217217
assertTrue(this.resolver.supportsParameter(parameter));
218218
thrown.expect(MessageConversionException.class);
219-
thrown.expectMessage("the payload is empty");
219+
thrown.expectMessage("payload is empty");
220220
thrown.expectMessage(Integer.class.getName());
221221
thrown.expectMessage(String.class.getName());
222222
this.resolver.resolveArgument(parameter, message);

0 commit comments

Comments
 (0)