Proper exception for controller method return types that do not work with MvcUriComponentsBuilder (e.g. final classes)

Includes direct use of ControllerMethodInvocationInterceptor for return type Object, avoiding the attempt to generate an Object subclass.

Issue: SPR-16710
This commit is contained in:
Juergen Hoeller
2018-04-11 16:05:54 +02:00
parent a6885c7235
commit f28a5d0cf7
2 changed files with 93 additions and 60 deletions

View File

@@ -253,10 +253,8 @@ public class MvcUriComponentsBuilder {
* controller.getAddressesForCountry("US")
* builder = MvcUriComponentsBuilder.fromMethodCall(controller);
* </pre>
*
* <p><strong>Note:</strong> This method extracts values from "Forwarded"
* and "X-Forwarded-*" headers if found. See class-level docs.
*
* @param info either the value returned from a "mock" controller
* invocation or the "mock" controller itself after an invocation
* @return a UriComponents instance
@@ -610,7 +608,11 @@ public class MvcUriComponentsBuilder {
@SuppressWarnings("unchecked")
private static <T> T initProxy(Class<?> type, ControllerMethodInvocationInterceptor interceptor) {
if (type.isInterface()) {
if (type == Object.class) {
return (T) interceptor;
}
else if (type.isInterface()) {
ProxyFactory factory = new ProxyFactory(EmptyTargetSource.INSTANCE);
factory.addInterface(type);
factory.addInterface(MethodInvocationInfo.class);
@@ -709,8 +711,18 @@ public class MvcUriComponentsBuilder {
}
public interface MethodInvocationInfo {
Class<?> getControllerType();
Method getControllerMethod();
Object[] getArgumentValues();
}
private static class ControllerMethodInvocationInterceptor
implements org.springframework.cglib.proxy.MethodInterceptor, MethodInterceptor {
implements org.springframework.cglib.proxy.MethodInterceptor, MethodInterceptor, MethodInvocationInfo {
private final Class<?> controllerType;
@@ -727,15 +739,15 @@ public class MvcUriComponentsBuilder {
@Override
@Nullable
public Object intercept(Object obj, Method method, Object[] args, @Nullable MethodProxy proxy) {
if (method.getName().equals("getControllerMethod")) {
if (method.getName().equals("getControllerType")) {
return this.controllerType;
}
else if (method.getName().equals("getControllerMethod")) {
return this.controllerMethod;
}
else if (method.getName().equals("getArgumentValues")) {
return this.argumentValues;
}
else if (method.getName().equals("getControllerType")) {
return this.controllerType;
}
else if (ReflectionUtils.isObjectMethod(method)) {
return ReflectionUtils.invokeMethod(method, obj, args);
}
@@ -743,7 +755,13 @@ public class MvcUriComponentsBuilder {
this.controllerMethod = method;
this.argumentValues = args;
Class<?> returnType = method.getReturnType();
return (void.class == returnType ? null : returnType.cast(initProxy(returnType, this)));
try {
return (returnType == void.class ? null : returnType.cast(initProxy(returnType, this)));
}
catch (Throwable ex) {
throw new IllegalStateException(
"Failed to create proxy for controller method return type: " + method, ex);
}
}
}
@@ -752,16 +770,23 @@ public class MvcUriComponentsBuilder {
public Object invoke(org.aopalliance.intercept.MethodInvocation inv) throws Throwable {
return intercept(inv.getThis(), inv.getMethod(), inv.getArguments(), null);
}
}
@Override
public Class<?> getControllerType() {
return this.controllerType;
}
public interface MethodInvocationInfo {
@Override
public Method getControllerMethod() {
Assert.state(this.controllerMethod != null, "Not initialized yet");
return this.controllerMethod;
}
Method getControllerMethod();
Object[] getArgumentValues();
Class<?> getControllerType();
@Override
public Object[] getArgumentValues() {
Assert.state(this.argumentValues != null, "Not initialized yet");
return this.argumentValues;
}
}