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.
+
+
+