Skip to content

Commit d32f577

Browse files
committed
Merge pull request #799 from ractive/master
2 parents 5e8d838 + 85cf4e6 commit d32f577

File tree

2 files changed

+103
-29
lines changed

2 files changed

+103
-29
lines changed

spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/MvcUriComponentsBuilder.java

Lines changed: 90 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,9 @@ public static UriComponentsBuilder fromController(Class<?> controllerType) {
159159
* @param controllerType the controller to build a URI for
160160
* @return a UriComponentsBuilder instance (never {@code null})
161161
*/
162-
public static UriComponentsBuilder fromController(UriComponentsBuilder builder, Class<?> controllerType) {
162+
public static UriComponentsBuilder fromController(UriComponentsBuilder builder,
163+
Class<?> controllerType) {
164+
163165
builder = getBaseUrlToUse(builder);
164166
String mapping = getTypeRequestMapping(controllerType);
165167
return builder.path(mapping);
@@ -176,8 +178,11 @@ public static UriComponentsBuilder fromController(UriComponentsBuilder builder,
176178
* @throws IllegalArgumentException if there is no matching or
177179
* if there is more than one matching method
178180
*/
179-
public static UriComponentsBuilder fromMethodName(Class<?> controllerType, String methodName, Object... args) {
180-
return fromMethodName(null, controllerType, methodName, args);
181+
public static UriComponentsBuilder fromMethodName(Class<?> controllerType,
182+
String methodName, Object... args) {
183+
184+
Method method = getMethod(controllerType, methodName, args);
185+
return fromMethodInternal(null, controllerType, method, args);
181186
}
182187

183188
/**
@@ -198,7 +203,7 @@ public static UriComponentsBuilder fromMethodName(UriComponentsBuilder builder,
198203
Class<?> controllerType, String methodName, Object... args) {
199204

200205
Method method = getMethod(controllerType, methodName, args);
201-
return fromMethod(builder, method, args);
206+
return fromMethodInternal(builder, controllerType, method, args);
202207
}
203208

204209
/**
@@ -232,12 +237,17 @@ public static UriComponentsBuilder fromMethodName(UriComponentsBuilder builder,
232237
* controller.getAddressesForCountry("US")
233238
* builder = MvcUriComponentsBuilder.fromMethodCall(controller);
234239
* </pre>
235-
* @param invocationInfo either the value returned from a "mock" controller
240+
* @param info either the value returned from a "mock" controller
236241
* invocation or the "mock" controller itself after an invocation
237242
* @return a UriComponents instance
238243
*/
239-
public static UriComponentsBuilder fromMethodCall(Object invocationInfo) {
240-
return fromMethodCall(null, invocationInfo);
244+
public static UriComponentsBuilder fromMethodCall(Object info) {
245+
Assert.isInstanceOf(MethodInvocationInfo.class, info);
246+
MethodInvocationInfo invocationInfo = (MethodInvocationInfo) info;
247+
Class<?> controllerType = invocationInfo.getControllerType();
248+
Method method = invocationInfo.getControllerMethod();
249+
Object[] arguments = invocationInfo.getArgumentValues();
250+
return fromMethodInternal(null, controllerType, method, arguments);
241251
}
242252

243253
/**
@@ -247,14 +257,17 @@ public static UriComponentsBuilder fromMethodCall(Object invocationInfo) {
247257
* request or to apply a custom baseUrl not matching the current request.
248258
* @param builder the builder for the base URL; the builder will be cloned
249259
* and therefore not modified and may be re-used for further calls.
250-
* @param invocationInfo either the value returned from a "mock" controller
260+
* @param info either the value returned from a "mock" controller
251261
* invocation or the "mock" controller itself after an invocation
252262
* @return a UriComponents instance
253263
*/
254-
public static UriComponentsBuilder fromMethodCall(UriComponentsBuilder builder, Object invocationInfo) {
255-
Assert.isInstanceOf(MethodInvocationInfo.class, invocationInfo);
256-
MethodInvocationInfo info = (MethodInvocationInfo) invocationInfo;
257-
return fromMethod(builder, info.getControllerMethod(), info.getArgumentValues());
264+
public static UriComponentsBuilder fromMethodCall(UriComponentsBuilder builder, Object info) {
265+
Assert.isInstanceOf(MethodInvocationInfo.class, info);
266+
MethodInvocationInfo invocationInfo = (MethodInvocationInfo) info;
267+
Class<?> controllerType = invocationInfo.getControllerType();
268+
Method method = invocationInfo.getControllerMethod();
269+
Object[] arguments = invocationInfo.getArgumentValues();
270+
return fromMethodInternal(builder, controllerType, method, arguments);
258271
}
259272

260273
/**
@@ -330,7 +343,10 @@ public static MethodArgumentBuilder fromMappingName(UriComponentsBuilder builder
330343
throw new IllegalArgumentException("No unique match for mapping mappingName " +
331344
name + ": " + handlerMethods);
332345
}
333-
return new MethodArgumentBuilder(builder, handlerMethods.get(0).getMethod());
346+
HandlerMethod handlerMethod = handlerMethods.get(0);
347+
Class<?> controllerType = handlerMethod.getBeanType();
348+
Method method = handlerMethod.getMethod();
349+
return new MethodArgumentBuilder(builder, controllerType, method);
334350
}
335351

336352
/**
@@ -341,12 +357,13 @@ public static MethodArgumentBuilder fromMappingName(UriComponentsBuilder builder
341357
* {@link org.springframework.web.method.support.UriComponentsContributor
342358
* UriComponentsContributor}) while remaining argument values are ignored and
343359
* can be {@code null}.
360+
* @param controllerType the controller type
344361
* @param method the controller method
345362
* @param args argument values for the controller method
346363
* @return a UriComponentsBuilder instance, never {@code null}
347364
*/
348-
public static UriComponentsBuilder fromMethod(Method method, Object... args) {
349-
return fromMethod(null, method, args);
365+
public static UriComponentsBuilder fromMethod(Class<?> controllerType, Method method, Object... args) {
366+
return fromMethodInternal(null, controllerType, method, args);
350367
}
351368

352369
/**
@@ -357,13 +374,32 @@ public static UriComponentsBuilder fromMethod(Method method, Object... args) {
357374
* current request.
358375
* @param baseUrl the builder for the base URL; the builder will be cloned
359376
* and therefore not modified and may be re-used for further calls.
377+
* @param controllerType the controller type
360378
* @param method the controller method
361379
* @param args argument values for the controller method
362380
* @return a UriComponentsBuilder instance, never {@code null}
363381
*/
364-
public static UriComponentsBuilder fromMethod(UriComponentsBuilder baseUrl, Method method, Object... args) {
382+
public static UriComponentsBuilder fromMethod(UriComponentsBuilder baseUrl,
383+
Class<?> controllerType, Method method, Object... args) {
384+
385+
return fromMethodInternal(baseUrl, method.getDeclaringClass(), method, args);
386+
}
387+
388+
/**
389+
* See {@link #fromMethod(Class, Method, Object...)}.
390+
* @deprecated as of 4.2 this is deprecated in favor of the overloaded
391+
* method that also accepts a controllerType.
392+
*/
393+
@Deprecated
394+
public static UriComponentsBuilder fromMethod(Method method, Object... args) {
395+
return fromMethodInternal(null, method.getDeclaringClass(), method, args);
396+
}
397+
398+
private static UriComponentsBuilder fromMethodInternal(UriComponentsBuilder baseUrl,
399+
Class<?> controllerType, Method method, Object... args) {
400+
365401
baseUrl = getBaseUrlToUse(baseUrl);
366-
String typePath = getTypeRequestMapping(method.getDeclaringClass());
402+
String typePath = getTypeRequestMapping(controllerType);
367403
String methodPath = getMethodRequestMapping(method);
368404
String path = pathMatcher.combine(typePath, methodPath);
369405
baseUrl.path(path);
@@ -560,7 +596,7 @@ public static <T> T on(Class<T> controllerType) {
560596
*/
561597
public static <T> T controller(Class<T> controllerType) {
562598
Assert.notNull(controllerType, "'controllerType' must not be null");
563-
return initProxy(controllerType, new ControllerMethodInvocationInterceptor());
599+
return initProxy(controllerType, new ControllerMethodInvocationInterceptor(controllerType));
564600
}
565601

566602
@SuppressWarnings("unchecked")
@@ -617,11 +653,11 @@ public MethodArgumentBuilder withMappingName(String mappingName) {
617653
}
618654

619655
/**
620-
* An alternative to {@link #fromMethod(java.lang.reflect.Method, Object...)}
656+
* An alternative to {@link #fromMethod(Class, Method, Object...)}
621657
* for use with an instance of this class created via {@link #relativeTo}.
622658
*/
623-
public UriComponentsBuilder withMethod(Method method, Object... args) {
624-
return fromMethod(this.baseUrl, method, args);
659+
public UriComponentsBuilder withMethod(Class<?> controllerType, Method method, Object... args) {
660+
return fromMethod(this.baseUrl, controllerType, method, args);
625661
}
626662

627663

@@ -634,11 +670,19 @@ private static class ControllerMethodInvocationInterceptor
634670
private static final Method getArgumentValues =
635671
ReflectionUtils.findMethod(MethodInvocationInfo.class, "getArgumentValues");
636672

673+
private static final Method getControllerType =
674+
ReflectionUtils.findMethod(MethodInvocationInfo.class, "getControllerType");
675+
637676
private Method controllerMethod;
638677

639678
private Object[] argumentValues;
640679

680+
private Class<?> controllerType;
641681

682+
ControllerMethodInvocationInterceptor(Class<?> controllerType) {
683+
this.controllerType = controllerType;
684+
}
685+
642686
@Override
643687
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) {
644688
if (getControllerMethod.equals(method)) {
@@ -647,6 +691,9 @@ public Object intercept(Object obj, Method method, Object[] args, MethodProxy pr
647691
else if (getArgumentValues.equals(method)) {
648692
return this.argumentValues;
649693
}
694+
else if (getControllerType.equals(method)) {
695+
return this.controllerType;
696+
}
650697
else if (ReflectionUtils.isObjectMethod(method)) {
651698
return ReflectionUtils.invokeMethod(method, obj, args);
652699
}
@@ -670,32 +717,46 @@ public interface MethodInvocationInfo {
670717
Method getControllerMethod();
671718

672719
Object[] getArgumentValues();
720+
721+
Class<?> getControllerType();
673722
}
674723

675724

676725
public static class MethodArgumentBuilder {
677726

727+
private final Class<?> controllerType;
728+
678729
private final Method method;
679730

680731
private final Object[] argumentValues;
681732

682733
private final UriComponentsBuilder baseUrl;
683734

684735

685-
public MethodArgumentBuilder(Method method) {
686-
this(null, method);
736+
public MethodArgumentBuilder(Class<?> controllerType, Method method) {
737+
this(null, controllerType, method);
687738
}
688739

689-
public MethodArgumentBuilder(UriComponentsBuilder baseUrl, Method method) {
740+
public MethodArgumentBuilder(UriComponentsBuilder baseUrl, Class<?> controllerType, Method method) {
741+
Assert.notNull(controllerType, "'controllerType' is required");
690742
Assert.notNull(method, "'method' is required");
691743
this.baseUrl = baseUrl;
744+
this.controllerType = controllerType;
692745
this.method = method;
693746
this.argumentValues = new Object[method.getParameterTypes().length];
694747
for (int i = 0; i < this.argumentValues.length; i++) {
695748
this.argumentValues[i] = null;
696749
}
697750
}
698751

752+
/**
753+
* @deprecated as of 4.2 deprecated in favor of alternative constructors
754+
* that accept the controllerType.
755+
*/
756+
@Deprecated
757+
public MethodArgumentBuilder(Method method) {
758+
this(method.getDeclaringClass(), method);
759+
}
699760

700761

701762
public MethodArgumentBuilder arg(int index, Object value) {
@@ -704,13 +765,13 @@ public MethodArgumentBuilder arg(int index, Object value) {
704765
}
705766

706767
public String build() {
707-
return MvcUriComponentsBuilder.fromMethod(this.baseUrl, this.method, this.argumentValues)
708-
.build(false).encode().toUriString();
768+
return fromMethodInternal(this.baseUrl, this.controllerType, this.method,
769+
this.argumentValues).build(false).encode().toUriString();
709770
}
710771

711-
public String buildAndExpand(Object... uriVariables) {
712-
return MvcUriComponentsBuilder.fromMethod(this.baseUrl, this.method, this.argumentValues)
713-
.build(false).expand(uriVariables).encode().toString();
772+
public String buildAndExpand(Object... uriVars) {
773+
return fromMethodInternal(this.baseUrl, this.controllerType, this.method,
774+
this.argumentValues).build(false).expand(uriVars).encode().toString();
714775
}
715776
}
716777

spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/MvcUriComponentsBuilderTests.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,14 @@ public void testFromMethodCall() {
229229
assertThat(uriComponents.toUriString(), endsWith("/something/else"));
230230
}
231231

232+
@Test
233+
public void testFromMethodCallOnSubclass() {
234+
UriComponents uriComponents = fromMethodCall(on(ExtendedController.class).myMethod(null)).build();
235+
236+
assertThat(uriComponents.toUriString(), startsWith("http://localhost"));
237+
assertThat(uriComponents.toUriString(), endsWith("/extended/else"));
238+
}
239+
232240
@Test
233241
public void testFromMethodCallWithTypeLevelUriVars() {
234242
UriComponents uriComponents = fromMethodCall(on(
@@ -418,6 +426,11 @@ HttpEntity<Void> methodWithMultiValueRequestParams(@PathVariable String id,
418426
}
419427
}
420428

429+
@RequestMapping("/extended")
430+
static class ExtendedController extends ControllerWithMethods {
431+
432+
}
433+
421434
@RequestMapping("/user/{userId}/contacts")
422435
static class UserContactController {
423436

0 commit comments

Comments
 (0)