Revise target bean exception with consistent message formatting

This commit is contained in:
Juergen Hoeller
2026-04-08 15:28:29 +02:00
parent 923ec6a8e1
commit 63cd96fa5f
3 changed files with 49 additions and 50 deletions
@@ -390,19 +390,18 @@ public class HandlerMethod extends AnnotatedMethod {
// Support methods for use in subclass variants
/**
* Assert that the target bean class is an instance of the class where the given
* method is declared. In some cases the actual controller instance at request-
* processing time may be a JDK dynamic proxy (lazy initialization, prototype
* beans, and others). {@code @Controller}'s that require proxying should prefer
* class-based proxy mechanisms.
* Assert that the target bean class is an instance of the class where the given method
* is declared. In some cases the actual controller instance at request-processing time
* may be a JDK dynamic proxy (lazy initialization, prototype beans, and others).
* {@code @Controller}'s that require proxying should prefer class-based proxy mechanisms.
*/
protected void assertTargetBean(Method method, Object targetBean, Object[] args) {
Class<?> methodDeclaringClass = method.getDeclaringClass();
Class<?> targetBeanClass = targetBean.getClass();
if (!methodDeclaringClass.isAssignableFrom(targetBeanClass)) {
String text = "The mapped handler method class '" + methodDeclaringClass.getName() +
"' is not an instance of the actual controller bean class '" +
targetBeanClass.getName() + "'. If the controller requires proxying " +
"' is not an instance of the actual target bean class '" +
targetBeanClass.getName() + "'. If the target bean requires proxying " +
"(for example, due to @Transactional), please use class-based proxying.";
throw new IllegalStateException(formatInvokeError(text, args));
}
@@ -410,12 +409,15 @@ public class HandlerMethod extends AnnotatedMethod {
protected String formatInvokeError(String text, Object[] args) {
String formattedArgs = IntStream.range(0, args.length)
.mapToObj(i -> (args[i] != null ?
"[" + i + "] [type=" + args[i].getClass().getName() + "] [value=" + args[i] + "]" :
"[" + i + "] [null]"))
.collect(Collectors.joining(",\n", " ", " "));
.mapToObj(i -> {
Object arg = args[i];
return (arg != null ?
"[" + i + "] [type=" + arg.getClass().getName() + "] [value=" + arg + "]" :
"[" + i + "] [null]");
})
.collect(Collectors.joining(",\n"));
return text + "\n" +
"Controller [" + getBeanType().getName() + "]\n" +
"Handler [" + getBeanType().getName() + "]\n" +
"Method [" + getBridgedMethod().toGenericString() + "] " +
"with argument values:\n" + formattedArgs;
}
@@ -49,7 +49,7 @@ class InvocableHandlerMethodTests {
@BeforeEach
void setUp() {
void setup() {
this.request = new ServletWebRequest(new MockHttpServletRequest(), new MockHttpServletResponse());
}
@@ -82,9 +82,9 @@ class InvocableHandlerMethodTests {
@Test
void cannotResolveArg() {
assertThatIllegalStateException().isThrownBy(() ->
getInvocable(Integer.class, String.class).invokeForRequest(request, null))
.withMessageContaining("Could not resolve parameter [0]");
assertThatIllegalStateException()
.isThrownBy(() -> getInvocable(Integer.class, String.class).invokeForRequest(request, null))
.withMessageContaining("Could not resolve parameter [0]");
}
@Test
@@ -108,54 +108,53 @@ class InvocableHandlerMethodTests {
@Test
void exceptionInResolvingArg() {
this.composite.addResolver(new ExceptionRaisingArgumentResolver());
assertThatIllegalArgumentException().isThrownBy(() ->
getInvocable(Integer.class, String.class).invokeForRequest(request, null));
assertThatIllegalArgumentException()
.isThrownBy(() -> getInvocable(Integer.class, String.class).invokeForRequest(request, null));
}
@Test
void illegalArgumentException() {
this.composite.addResolver(new StubArgumentResolver(Integer.class, "__not_an_int__"));
this.composite.addResolver(new StubArgumentResolver("value"));
assertThatIllegalStateException().isThrownBy(() ->
getInvocable(Integer.class, String.class).invokeForRequest(request, null))
.withCauseInstanceOf(IllegalArgumentException.class)
.withMessageContaining("Controller [")
.withMessageContaining("Method [")
.withMessageContaining("with argument values:")
.withMessageContaining("[0] [type=java.lang.String] [value=__not_an_int__]")
.withMessageContaining("[1] [type=java.lang.String] [value=value");
assertThatIllegalStateException()
.isThrownBy(() -> getInvocable(Integer.class, String.class).invokeForRequest(request, null))
.withCauseInstanceOf(IllegalArgumentException.class)
.withMessageContaining("[" + Handler.class.getName() + "]")
.withMessageContaining(Handler.class.getName() + ".handle")
.withMessageContaining("[0] [type=java.lang.String] [value=__not_an_int__]")
.withMessageContaining("[1] [type=java.lang.String] [value=value");
}
@Test
void invocationTargetException() {
RuntimeException runtimeException = new RuntimeException("error");
assertThatRuntimeException()
.isThrownBy(() -> getInvocable(Throwable.class).invokeForRequest(this.request, null, runtimeException))
.isSameAs(runtimeException);
.isThrownBy(() -> getInvocable(Throwable.class).invokeForRequest(this.request, null, runtimeException))
.isSameAs(runtimeException);
Error error = new Error("error");
assertThatExceptionOfType(Error.class)
.isThrownBy(() -> getInvocable(Throwable.class).invokeForRequest(this.request, null, error))
.isSameAs(error);
.isThrownBy(() -> getInvocable(Throwable.class).invokeForRequest(this.request, null, error))
.isSameAs(error);
Exception exception = new Exception("error");
assertThatException()
.isThrownBy(() -> getInvocable(Throwable.class).invokeForRequest(this.request, null, exception))
.isSameAs(exception);
.isThrownBy(() -> getInvocable(Throwable.class).invokeForRequest(this.request, null, exception))
.isSameAs(exception);
Throwable throwable = new Throwable("error");
assertThatIllegalStateException()
.isThrownBy(() -> getInvocable(Throwable.class).invokeForRequest(this.request, null, throwable))
.withCause(throwable)
.withMessageContaining("Invocation failure");
.isThrownBy(() -> getInvocable(Throwable.class).invokeForRequest(this.request, null, throwable))
.withCause(throwable)
.withMessageContaining("Invocation failure");
}
@Test // SPR-13917
void invocationErrorMessage() {
this.composite.addResolver(new StubArgumentResolver(double.class));
assertThatIllegalStateException()
.isThrownBy(() -> getInvocable(double.class).invokeForRequest(this.request, null))
.withMessageContaining("Illegal argument");
.isThrownBy(() -> getInvocable(double.class).invokeForRequest(this.request, null))
.withMessageContaining("Illegal argument");
}
private InvocableHandlerMethod getInvocable(Class<?>... argTypes) {
@@ -170,7 +169,6 @@ class InvocableHandlerMethodTests {
}
@SuppressWarnings("unused")
private static class Handler {
@@ -117,8 +117,8 @@ class InvocableHandlerMethodTests {
Mono<HandlerResult> mono = invoke(new TestController(), method);
assertThatIllegalStateException()
.isThrownBy(mono::block)
.withMessage("Could not resolve parameter [0] in %s: No suitable resolver", method.toGenericString());
.isThrownBy(mono::block)
.withMessage("Could not resolve parameter [0] in %s: No suitable resolver", method.toGenericString());
}
@Test
@@ -145,8 +145,8 @@ class InvocableHandlerMethodTests {
Mono<HandlerResult> mono = invoke(new TestController(), method);
assertThatExceptionOfType(UnsupportedMediaTypeStatusException.class)
.isThrownBy(mono::block)
.withMessage("415 UNSUPPORTED_MEDIA_TYPE \"boo\"");
.isThrownBy(mono::block)
.withMessage("415 UNSUPPORTED_MEDIA_TYPE \"boo\"");
}
@Test
@@ -156,12 +156,11 @@ class InvocableHandlerMethodTests {
Mono<HandlerResult> mono = invoke(new TestController(), method);
assertThatIllegalStateException()
.isThrownBy(mono::block)
.withCauseInstanceOf(IllegalArgumentException.class)
.withMessageContaining("Controller [")
.withMessageContaining("Method [")
.withMessageContaining("with argument values:")
.withMessageContaining("[0] [type=java.lang.Integer] [value=1]");
.isThrownBy(mono::block)
.withCauseInstanceOf(IllegalArgumentException.class)
.withMessageContaining("[" + TestController.class.getName() + "]")
.withMessageContaining(TestController.class.getName() + ".singleArg")
.withMessageContaining("[0] [type=java.lang.Integer] [value=1]");
}
@Test
@@ -170,8 +169,8 @@ class InvocableHandlerMethodTests {
Mono<HandlerResult> mono = invoke(new TestController(), method);
assertThatIllegalStateException()
.isThrownBy(mono::block)
.withMessage("boo");
.isThrownBy(mono::block)
.withMessage("boo");
}
@Test