From 7bc9667913571d17c38f2c1854af9eebadffa832 Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Mon, 7 Jan 2013 18:03:40 -0500 Subject: [PATCH] Add support for placeholders in @RequestMapping @RequestMapping annotations now support ${...} placeholders. Issue: SPR-9935 --- .../web/bind/annotation/RequestMapping.java | 1 + .../RequestMappingHandlerMapping.java | 36 ++++++++++++++++--- .../RequestMappingHandlerMappingTests.java | 17 ++++++++- src/reference/docbook/mvc.xml | 12 +++++++ 4 files changed, 61 insertions(+), 5 deletions(-) diff --git a/spring-web/src/main/java/org/springframework/web/bind/annotation/RequestMapping.java b/spring-web/src/main/java/org/springframework/web/bind/annotation/RequestMapping.java index ff4849e35e26..320bb95d1335 100644 --- a/spring-web/src/main/java/org/springframework/web/bind/annotation/RequestMapping.java +++ b/spring-web/src/main/java/org/springframework/web/bind/annotation/RequestMapping.java @@ -266,6 +266,7 @@ * Ant-style path patterns are also supported (e.g. "/myPath/*.do"). * At the method level, relative paths (e.g. "edit.do") are supported * within the primary mapping expressed at the type level. + * Path mapping URIs may contain placeholders (e.g. "/${connect}") *

In a Portlet environment: the mapped portlet modes * (i.e. "EDIT", "VIEW", "HELP" or any custom modes). *

Supported at the type level as well as at the method level! diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMapping.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMapping.java index 315cf8f68e86..5700b8def72e 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMapping.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMapping.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,9 +20,11 @@ import java.util.ArrayList; import java.util.List; +import org.springframework.context.EmbeddedValueResolverAware; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.stereotype.Controller; import org.springframework.util.Assert; +import org.springframework.util.StringValueResolver; import org.springframework.web.accept.ContentNegotiationManager; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.servlet.mvc.condition.AbstractRequestCondition; @@ -46,7 +48,8 @@ * @author Rossen Stoyanchev * @since 3.1 */ -public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMapping { +public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMapping + implements EmbeddedValueResolverAware { private boolean useSuffixPatternMatch = true; @@ -58,6 +61,8 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi private final List fileExtensions = new ArrayList(); + private StringValueResolver embeddedValueResolver; + /** * Whether to use suffix pattern match (".*") when matching patterns to @@ -101,6 +106,11 @@ public void setUseTrailingSlashMatch(boolean useTrailingSlashMatch) { this.useTrailingSlashMatch = useTrailingSlashMatch; } + @Override + public void setEmbeddedValueResolver(StringValueResolver resolver) { + this.embeddedValueResolver = resolver; + } + /** * Set the {@link ContentNegotiationManager} to use to determine requested media types. * If not set, the default constructor is used. @@ -142,7 +152,7 @@ public ContentNegotiationManager getContentNegotiationManager() { * Return the file extensions to use for suffix pattern matching. */ public List getFileExtensions() { - return fileExtensions; + return this.fileExtensions; } @Override @@ -227,8 +237,9 @@ protected RequestCondition getCustomMethodCondition(Method method) { * Created a RequestMappingInfo from a RequestMapping annotation. */ private RequestMappingInfo createRequestMappingInfo(RequestMapping annotation, RequestCondition customCondition) { + String[] patterns = resolveEmbeddedValuesInPatterns(annotation.value()); return new RequestMappingInfo( - new PatternsRequestCondition(annotation.value(), getUrlPathHelper(), getPathMatcher(), + new PatternsRequestCondition(patterns, getUrlPathHelper(), getPathMatcher(), this.useSuffixPatternMatch, this.useTrailingSlashMatch, this.fileExtensions), new RequestMethodsRequestCondition(annotation.method()), new ParamsRequestCondition(annotation.params()), @@ -238,4 +249,21 @@ private RequestMappingInfo createRequestMappingInfo(RequestMapping annotation, R customCondition); } + /** + * Resolve placeholder values in the given array of patterns. + * @return a new array with updated patterns + */ + protected String[] resolveEmbeddedValuesInPatterns(String[] patterns) { + if (this.embeddedValueResolver == null) { + return patterns; + } + else { + String[] resolvedPatterns = new String[patterns.length]; + for (int i=0; i < patterns.length; i++) { + resolvedPatterns[i] = this.embeddedValueResolver.resolveStringValue(patterns[i]); + } + return resolvedPatterns; + } + } + } diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMappingTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMappingTests.java index b5a6bd139e5f..5c4d0776a498 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMappingTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMappingTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,6 +24,7 @@ import org.junit.Before; import org.junit.Test; import org.springframework.http.MediaType; +import org.springframework.util.StringValueResolver; import org.springframework.web.accept.ContentNegotiationManager; import org.springframework.web.accept.PathExtensionContentNegotiationStrategy; import org.springframework.web.context.support.StaticWebApplicationContext; @@ -78,4 +79,18 @@ public void useSuffixPatternMatch() { this.handlerMapping.useSuffixPatternMatch()); } + @Test + public void resolveEmbeddedValuesInPatterns() { + this.handlerMapping.setEmbeddedValueResolver(new StringValueResolver() { + public String resolveStringValue(String value) { + return "/${pattern}/bar".equals(value) ? "/foo/bar" : value; + } + }); + + String[] patterns = new String[] { "/foo", "/${pattern}/bar" }; + String[] result = this.handlerMapping.resolveEmbeddedValuesInPatterns(patterns); + + assertArrayEquals(new String[] { "/foo", "/foo/bar" }, result); + } + } diff --git a/src/reference/docbook/mvc.xml b/src/reference/docbook/mvc.xml index ffe081c1e09b..279a76ec159d 100644 --- a/src/reference/docbook/mvc.xml +++ b/src/reference/docbook/mvc.xml @@ -1073,6 +1073,18 @@ public class RelativePathUriTemplateController { /owners/*/pets/{petId}). +

+ Patterns with Placeholders + + Patterns in @RequestMapping annotations + support ${...} placeholders against local properties and/or system properties + and environment variables. This may be useful in cases where the path a + controller is mapped to may need to be customized through configuration. + For more information on placeholders see the Javadoc for + PropertyPlaceholderConfigurer. + +
+
Matrix Variables