diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/MvcUriComponentsBuilder.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/MvcUriComponentsBuilder.java index fbf4bbcb1d9..a2ea58bf5a3 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/MvcUriComponentsBuilder.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/MvcUriComponentsBuilder.java @@ -31,7 +31,6 @@ import org.apache.commons.logging.LogFactory; import org.springframework.aop.framework.ProxyFactory; import org.springframework.aop.target.EmptyTargetSource; import org.springframework.beans.factory.NoSuchBeanDefinitionException; -import org.springframework.beans.factory.NoUniqueBeanDefinitionException; import org.springframework.cglib.core.SpringNamingPolicy; import org.springframework.cglib.proxy.Callback; import org.springframework.cglib.proxy.Enhancer; @@ -175,7 +174,7 @@ public class MvcUriComponentsBuilder { Class> controllerType) { builder = getBaseUrlToUse(builder); - String mapping = getTypeRequestMapping(controllerType); + String mapping = getClassMapping(controllerType); return builder.path(mapping); } @@ -222,6 +221,49 @@ public class MvcUriComponentsBuilder { return fromMethodInternal(builder, controllerType, method, args); } + /** + * Create a {@link UriComponentsBuilder} from the mapping of a controller method + * and an array of method argument values. The array of values must match the + * signature of the controller method. Values for {@code @RequestParam} and + * {@code @PathVariable} are used for building the URI (via implementations of + * {@link org.springframework.web.method.support.UriComponentsContributor + * UriComponentsContributor}) while remaining argument values are ignored and + * can be {@code null}. + *
Note: This method extracts values from "Forwarded" + * and "X-Forwarded-*" headers if found. See class-level docs. + * @param controllerType the controller type + * @param method the controller method + * @param args argument values for the controller method + * @return a UriComponentsBuilder instance, never {@code null} + * @since 4.2 + */ + public static UriComponentsBuilder fromMethod(Class> controllerType, Method method, Object... args) { + return fromMethodInternal(null, controllerType, method, args); + } + + /** + * An alternative to {@link #fromMethod(Class, Method, Object...)} + * that accepts a {@code UriComponentsBuilder} representing the base URL. + * This is useful when using MvcUriComponentsBuilder outside the context of + * processing a request or to apply a custom baseUrl not matching the + * current request. + *
Note: This method extracts values from "Forwarded" + * and "X-Forwarded-*" headers if found. See class-level docs. + * @param baseUrl the builder for the base URL; the builder will be cloned + * and therefore not modified and may be re-used for further calls. + * @param controllerType the controller type + * @param method the controller method + * @param args argument values for the controller method + * @return a UriComponentsBuilder instance (never {@code null}) + * @since 4.2 + */ + public static UriComponentsBuilder fromMethod(UriComponentsBuilder baseUrl, + @Nullable Class> controllerType, Method method, Object... args) { + + return fromMethodInternal(baseUrl, + (controllerType != null ? controllerType : method.getDeclaringClass()), method, args); + } + /** * Create a {@link UriComponentsBuilder} by invoking a "mock" controller method. * The controller method and the supplied argument values are then used to @@ -258,6 +300,8 @@ public class MvcUriComponentsBuilder { * @param info either the value returned from a "mock" controller * invocation or the "mock" controller itself after an invocation * @return a UriComponents instance + * @see #on(Class) + * @see #controller(Class) */ public static UriComponentsBuilder fromMethodCall(Object info) { Assert.isInstanceOf(MethodInvocationInfo.class, info, "MethodInvocationInfo required"); @@ -290,6 +334,50 @@ public class MvcUriComponentsBuilder { return fromMethodInternal(builder, controllerType, method, arguments); } + /** + * Return a "mock" controller instance. When an {@code @RequestMapping} method + * on the controller is invoked, the supplied argument values are remembered + * and the result can then be used to create a {@code UriComponentsBuilder} + * via {@link #fromMethodCall(Object)}. + *
Note that this is a shorthand version of {@link #controller(Class)} intended + * for inline use (with a static import), for example: + *
+ * MvcUriComponentsBuilder.fromMethodCall(on(FooController.class).getFoo(1)).build(); + *+ *
Note: This method extracts values from "Forwarded"
+ * and "X-Forwarded-*" headers if found. See class-level docs.
+ *
+ * @param controllerType the target controller
+ */
+ public static This is a longer version of {@link #on(Class)}. It is needed with controller
+ * methods returning void as well for repeated invocations.
+ * Note: This method extracts values from "Forwarded"
+ * and "X-Forwarded-*" headers if found. See class-level docs.
+ * @param controllerType the target controller
+ */
+ public static The configured
@@ -355,70 +443,89 @@ public class MvcUriComponentsBuilder {
* @since 4.2
*/
public static MethodArgumentBuilder fromMappingName(@Nullable UriComponentsBuilder builder, String name) {
- RequestMappingInfoHandlerMapping handlerMapping = getRequestMappingInfoHandlerMapping();
- List Note: This method extracts values from "Forwarded"
* and "X-Forwarded-*" headers if found. See class-level docs.
- * @param controllerType the controller type
- * @param method the controller method
- * @param args argument values for the controller method
- * @return a UriComponentsBuilder instance, never {@code null}
* @since 4.2
*/
- public static UriComponentsBuilder fromMethod(Class> controllerType, Method method, Object... args) {
- return fromMethodInternal(null, controllerType, method, args);
+ public UriComponentsBuilder withController(Class> controllerType) {
+ return fromController(this.baseUrl, controllerType);
}
/**
- * An alternative to {@link #fromMethod(Class, Method, Object...)}
- * that accepts a {@code UriComponentsBuilder} representing the base URL.
- * This is useful when using MvcUriComponentsBuilder outside the context of
- * processing a request or to apply a custom baseUrl not matching the
- * current request.
+ * An alternative to {@link #fromMethodName(Class, String, Object...)}} for
+ * use with an instance of this class created via {@link #relativeTo}.
* Note: This method extracts values from "Forwarded"
* and "X-Forwarded-*" headers if found. See class-level docs.
- * @param baseUrl the builder for the base URL; the builder will be cloned
- * and therefore not modified and may be re-used for further calls.
- * @param controllerType the controller type
- * @param method the controller method
- * @param args argument values for the controller method
- * @return a UriComponentsBuilder instance (never {@code null})
* @since 4.2
*/
- public static UriComponentsBuilder fromMethod(UriComponentsBuilder baseUrl,
- @Nullable Class> controllerType, Method method, Object... args) {
+ public UriComponentsBuilder withMethodName(Class> controllerType, String methodName, Object... args) {
+ return fromMethodName(this.baseUrl, controllerType, methodName, args);
+ }
- return fromMethodInternal(baseUrl,
- (controllerType != null ? controllerType : method.getDeclaringClass()), method, args);
+ /**
+ * An alternative to {@link #fromMethodCall(Object)} for use with an instance
+ * of this class created via {@link #relativeTo}.
+ * Note: This method extracts values from "Forwarded"
+ * and "X-Forwarded-*" headers if found. See class-level docs.
+ * @since 4.2
+ */
+ public UriComponentsBuilder withMethodCall(Object invocationInfo) {
+ return fromMethodCall(this.baseUrl, invocationInfo);
}
+ /**
+ * An alternative to {@link #fromMappingName(String)} for use with an instance
+ * of this class created via {@link #relativeTo}.
+ * Note: This method extracts values from "Forwarded"
+ * and "X-Forwarded-*" headers if found. See class-level docs.
+ * @since 4.2
+ */
+ public MethodArgumentBuilder withMappingName(String mappingName) {
+ return fromMappingName(this.baseUrl, mappingName);
+ }
+
+ /**
+ * An alternative to {@link #fromMethod(Class, Method, Object...)}
+ * for use with an instance of this class created via {@link #relativeTo}.
+ * Note: This method extracts values from "Forwarded"
+ * and "X-Forwarded-*" headers if found. See class-level docs.
+ * @since 4.2
+ */
+ public UriComponentsBuilder withMethod(Class> controllerType, Method method, Object... args) {
+ return fromMethod(this.baseUrl, controllerType, method, args);
+ }
+
+
private static UriComponentsBuilder fromMethodInternal(@Nullable UriComponentsBuilder baseUrl,
Class> controllerType, Method method, Object... args) {
baseUrl = getBaseUrlToUse(baseUrl);
- String typePath = getTypeRequestMapping(controllerType);
- String methodPath = getMethodRequestMapping(method);
+ String typePath = getClassMapping(controllerType);
+ String methodPath = getMethodMapping(method);
String path = pathMatcher.combine(typePath, methodPath);
baseUrl.path(path);
UriComponents uriComponents = applyContributors(baseUrl, method, args);
@@ -426,21 +533,18 @@ public class MvcUriComponentsBuilder {
}
private static UriComponentsBuilder getBaseUrlToUse(@Nullable UriComponentsBuilder baseUrl) {
- if (baseUrl != null) {
- return baseUrl.cloneBuilder();
- }
- else {
- return ServletUriComponentsBuilder.fromCurrentServletMapping();
- }
+ return baseUrl == null ?
+ ServletUriComponentsBuilder.fromCurrentServletMapping() :
+ baseUrl.cloneBuilder();
}
- private static String getTypeRequestMapping(Class> controllerType) {
+ private static String getClassMapping(Class> controllerType) {
Assert.notNull(controllerType, "'controllerType' must not be null");
- RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(controllerType, RequestMapping.class);
- if (requestMapping == null) {
+ RequestMapping mapping = AnnotatedElementUtils.findMergedAnnotation(controllerType, RequestMapping.class);
+ if (mapping == null) {
return "/";
}
- String[] paths = requestMapping.path();
+ String[] paths = mapping.path();
if (ObjectUtils.isEmpty(paths) || StringUtils.isEmpty(paths[0])) {
return "/";
}
@@ -450,7 +554,7 @@ public class MvcUriComponentsBuilder {
return paths[0];
}
- private static String getMethodRequestMapping(Method method) {
+ private static String getMethodMapping(Method method) {
Assert.notNull(method, "'method' must not be null");
RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(method, RequestMapping.class);
if (requestMapping == null) {
@@ -488,9 +592,9 @@ public class MvcUriComponentsBuilder {
}
private static UriComponents applyContributors(UriComponentsBuilder builder, Method method, Object... args) {
- CompositeUriComponentsContributor contributor = getConfiguredUriComponentsContributor();
+ CompositeUriComponentsContributor contributor = getUriComponentsContributor();
if (contributor == null) {
- logger.debug("Using default CompositeUriComponentsContributor");
+ logger.trace("Using default CompositeUriComponentsContributor");
contributor = defaultUriComponentsContributor;
}
@@ -509,11 +613,12 @@ public class MvcUriComponentsBuilder {
}
// We may not have all URI var values, expand only what we have
- return builder.build().expand(name -> uriVars.getOrDefault(name, UriComponents.UriTemplateVariables.SKIP_VALUE));
+ return builder.build().expand(name ->
+ uriVars.getOrDefault(name, UriComponents.UriTemplateVariables.SKIP_VALUE));
}
@Nullable
- private static CompositeUriComponentsContributor getConfiguredUriComponentsContributor() {
+ private static CompositeUriComponentsContributor getUriComponentsContributor() {
WebApplicationContext wac = getWebApplicationContext();
if (wac == null) {
return null;
@@ -522,193 +627,30 @@ public class MvcUriComponentsBuilder {
return wac.getBean(MVC_URI_COMPONENTS_CONTRIBUTOR_BEAN_NAME, CompositeUriComponentsContributor.class);
}
catch (NoSuchBeanDefinitionException ex) {
- if (logger.isDebugEnabled()) {
- logger.debug("No CompositeUriComponentsContributor bean with name '" +
- MVC_URI_COMPONENTS_CONTRIBUTOR_BEAN_NAME + "'");
+ if (logger.isTraceEnabled()) {
+ logger.trace("No CompositeUriComponentsContributor");
}
return null;
}
}
- private static RequestMappingInfoHandlerMapping getRequestMappingInfoHandlerMapping() {
- WebApplicationContext wac = getWebApplicationContext();
- Assert.notNull(wac, "Cannot lookup handler method mappings without WebApplicationContext");
- try {
- return wac.getBean(RequestMappingInfoHandlerMapping.class);
- }
- catch (NoUniqueBeanDefinitionException ex) {
- throw new IllegalStateException("More than one RequestMappingInfoHandlerMapping beans found", ex);
- }
- catch (NoSuchBeanDefinitionException ex) {
- throw new IllegalStateException("No RequestMappingInfoHandlerMapping bean", ex);
- }
- }
-
@Nullable
private static WebApplicationContext getWebApplicationContext() {
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
if (requestAttributes == null) {
- logger.debug("No request bound to the current thread: not in a DispatcherServlet request?");
+ logger.trace("No request bound to the current thread: not in a DispatcherServlet request?");
return null;
}
-
HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getRequest();
- WebApplicationContext wac = (WebApplicationContext)
- request.getAttribute(DispatcherServlet.WEB_APPLICATION_CONTEXT_ATTRIBUTE);
+ String attributeName = DispatcherServlet.WEB_APPLICATION_CONTEXT_ATTRIBUTE;
+ WebApplicationContext wac = (WebApplicationContext) request.getAttribute(attributeName);
if (wac == null) {
- logger.debug("No WebApplicationContext found: not in a DispatcherServlet request?");
+ logger.trace("No WebApplicationContext found: not in a DispatcherServlet request?");
return null;
}
return wac;
}
- /**
- * Return a "mock" controller instance. When an {@code @RequestMapping} method
- * on the controller is invoked, the supplied argument values are remembered
- * and the result can then be used to create a {@code UriComponentsBuilder}
- * via {@link #fromMethodCall(Object)}.
- * Note that this is a shorthand version of {@link #controller(Class)} intended
- * for inline use (with a static import), for example:
- * Note: This method extracts values from "Forwarded"
- * and "X-Forwarded-*" headers if found. See class-level docs.
- *
- * @param controllerType the target controller
- */
- public static This is a longer version of {@link #on(Class)}. It is needed with controller
- * methods returning void as well for repeated invocations.
- * Note: This method extracts values from "Forwarded"
- * and "X-Forwarded-*" headers if found. See class-level docs.
- * @param controllerType the target controller
- */
- public static Note: This method extracts values from "Forwarded"
- * and "X-Forwarded-*" headers if found. See class-level docs.
- * @since 4.2
- */
- public UriComponentsBuilder withController(Class> controllerType) {
- return fromController(this.baseUrl, controllerType);
- }
-
- /**
- * An alternative to {@link #fromMethodName(Class, String, Object...)}} for
- * use with an instance of this class created via {@link #relativeTo}.
- * Note: This method extracts values from "Forwarded"
- * and "X-Forwarded-*" headers if found. See class-level docs.
- * @since 4.2
- */
- public UriComponentsBuilder withMethodName(Class> controllerType, String methodName, Object... args) {
- return fromMethodName(this.baseUrl, controllerType, methodName, args);
- }
-
- /**
- * An alternative to {@link #fromMethodCall(Object)} for use with an instance
- * of this class created via {@link #relativeTo}.
- * Note: This method extracts values from "Forwarded"
- * and "X-Forwarded-*" headers if found. See class-level docs.
- * @since 4.2
- */
- public UriComponentsBuilder withMethodCall(Object invocationInfo) {
- return fromMethodCall(this.baseUrl, invocationInfo);
- }
-
- /**
- * An alternative to {@link #fromMappingName(String)} for use with an instance
- * of this class created via {@link #relativeTo}.
- * Note: This method extracts values from "Forwarded"
- * and "X-Forwarded-*" headers if found. See class-level docs.
- * @since 4.2
- */
- public MethodArgumentBuilder withMappingName(String mappingName) {
- return fromMappingName(this.baseUrl, mappingName);
- }
-
- /**
- * An alternative to {@link #fromMethod(Class, Method, Object...)}
- * for use with an instance of this class created via {@link #relativeTo}.
- * Note: This method extracts values from "Forwarded"
- * and "X-Forwarded-*" headers if found. See class-level docs.
- * @since 4.2
- */
- public UriComponentsBuilder withMethod(Class> controllerType, Method method, Object... args) {
- return fromMethod(this.baseUrl, controllerType, method, args);
- }
public interface MethodInvocationInfo {
@@ -787,6 +729,61 @@ public class MvcUriComponentsBuilder {
Assert.state(this.argumentValues != null, "Not initialized yet");
return this.argumentValues;
}
+
+
+ @SuppressWarnings("unchecked")
+ private static
+ * FooController fooController = controller(FooController.class);
+ *
+ * fooController.saveFoo(1, null);
+ * builder = MvcUriComponentsBuilder.fromMethodCall(fooController);
+ *
+ * fooController.saveFoo(2, null);
+ * builder = MvcUriComponentsBuilder.fromMethodCall(fooController);
+ *
+ *
- * MvcUriComponentsBuilder.fromMethodCall(on(FooController.class).getFoo(1)).build();
- *
- *
- * FooController fooController = controller(FooController.class);
- *
- * fooController.saveFoo(1, null);
- * builder = MvcUriComponentsBuilder.fromMethodCall(fooController);
- *
- * fooController.saveFoo(2, null);
- * builder = MvcUriComponentsBuilder.fromMethodCall(fooController);
- *
- *