Browse Source

Add RequestMappingInfoBuilder

Issue: SPR-11541
pull/1272/head
Rossen Stoyanchev 10 years ago
parent
commit
3abcea1296
  1. 286
      spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/RequestMappingInfo.java
  2. 72
      spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMapping.java

286
spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/RequestMappingInfo.java

@ -16,9 +16,13 @@ @@ -16,9 +16,13 @@
package org.springframework.web.servlet.mvc.method;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import org.springframework.util.PathMatcher;
import org.springframework.util.StringUtils;
import org.springframework.web.accept.ContentNegotiationManager;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.cors.CorsUtils;
import org.springframework.web.servlet.mvc.condition.ConsumesRequestCondition;
@ -29,6 +33,7 @@ import org.springframework.web.servlet.mvc.condition.ProducesRequestCondition; @@ -29,6 +33,7 @@ import org.springframework.web.servlet.mvc.condition.ProducesRequestCondition;
import org.springframework.web.servlet.mvc.condition.RequestCondition;
import org.springframework.web.servlet.mvc.condition.RequestConditionHolder;
import org.springframework.web.servlet.mvc.condition.RequestMethodsRequestCondition;
import org.springframework.web.util.UrlPathHelper;
/**
* Encapsulates the following request mapping conditions:
@ -330,4 +335,285 @@ public final class RequestMappingInfo implements RequestCondition<RequestMapping @@ -330,4 +335,285 @@ public final class RequestMappingInfo implements RequestCondition<RequestMapping
return builder.toString();
}
/**
* Create a new {@code RequestMappingInfo.Builder} with the given paths.
* @param paths the paths to use
* @since 4.2
*/
public static Builder paths(String... paths) {
return new DefaultBuilder(paths);
}
/**
* Defines a builder for creating a RequestMappingInfo.
* @since 4.2
*/
public interface Builder {
/**
* Set the path patterns.
*/
Builder paths(String... paths);
/**
* Set the request method conditions.
*/
Builder methods(RequestMethod... methods);
/**
* Set the request param conditions.
*/
Builder params(String... params);
/**
* Set the header conditions.
* <p>By default this is not set.
*/
Builder headers(String... headers);
/**
* Set the consumes conditions.
*/
Builder consumes(String... consumes);
/**
* Set the produces conditions.
*/
Builder produces(String... produces);
/**
* Set the mapping name.
*/
Builder mappingName(String name);
/**
* Set a custom conditions to use.
*/
Builder customCondition(RequestCondition<?> condition);
/**
* Provide additional configuration needed for request mapping purposes.
*/
Builder options(BuilderConfiguration options);
/**
* Build the RequestMappingInfo.
*/
RequestMappingInfo build();
}
private static class DefaultBuilder implements Builder {
private String[] paths;
private RequestMethod[] methods;
private String[] params;
private String[] headers;
private String[] consumes;
private String[] produces;
private String mappingName;
private RequestCondition<?> customCondition;
private BuilderConfiguration options = new BuilderConfiguration();
public DefaultBuilder(String... paths) {
this.paths = paths;
}
@Override
public Builder paths(String... paths) {
this.paths = paths;
return this;
}
@Override
public DefaultBuilder methods(RequestMethod... methods) {
this.methods = methods;
return this;
}
@Override
public DefaultBuilder params(String... params) {
this.params = params;
return this;
}
@Override
public DefaultBuilder headers(String... headers) {
this.headers = headers;
return this;
}
@Override
public DefaultBuilder consumes(String... consumes) {
this.consumes = consumes;
return this;
}
@Override
public DefaultBuilder produces(String... produces) {
this.produces = produces;
return this;
}
@Override
public DefaultBuilder mappingName(String name) {
this.mappingName = name;
return this;
}
@Override
public DefaultBuilder customCondition(RequestCondition<?> condition) {
this.customCondition = condition;
return this;
}
@Override
public Builder options(BuilderConfiguration options) {
this.options = options;
return this;
}
@Override
public RequestMappingInfo build() {
ContentNegotiationManager manager = this.options.getContentNegotiationManager();
PatternsRequestCondition patternsCondition = new PatternsRequestCondition(
this.paths, this.options.getUrlPathHelper(), this.options.getPathMatcher(),
this.options.useSuffixPatternMatch(), this.options.useTrailingSlashMatch(),
this.options.getFileExtensions());
return new RequestMappingInfo(this.mappingName, patternsCondition,
new RequestMethodsRequestCondition(methods),
new ParamsRequestCondition(this.params),
new HeadersRequestCondition(this.headers),
new ConsumesRequestCondition(this.consumes, this.headers),
new ProducesRequestCondition(this.produces, this.headers, manager),
this.customCondition);
}
}
/**
* Container for configuration options used for request mapping purposes.
* Such configuration is required to create RequestMappingInfo instances but
* is typically used across all RequestMappingInfo instances.
*
* @see Builder#options
* @since 4.2
*/
public static class BuilderConfiguration {
private UrlPathHelper urlPathHelper;
private PathMatcher pathMatcher;
private boolean trailingSlashMatch = true;
private boolean suffixPatternMatch = true;
private boolean registeredSuffixPatternMatch = false;
private ContentNegotiationManager contentNegotiationManager;
/**
* Set a custom UrlPathHelper to use for the PatternsRequestCondition.
* <p>By default this is not set.
*/
public void setPathHelper(UrlPathHelper pathHelper) {
this.urlPathHelper = pathHelper;
}
public UrlPathHelper getUrlPathHelper() {
return this.urlPathHelper;
}
/**
* Set a custom PathMatcher to use for the PatternsRequestCondition.
* <p>By default this is not set.
*/
public void setPathMatcher(PathMatcher pathMatcher) {
this.pathMatcher = pathMatcher;
}
public PathMatcher getPathMatcher() {
return this.pathMatcher;
}
/**
* Whether to apply trailing slash matching in PatternsRequestCondition.
* <p>By default this is set to 'true'.
*/
public void setTrailingSlashMatch(boolean trailingSlashMatch) {
this.trailingSlashMatch = trailingSlashMatch;
}
public boolean useTrailingSlashMatch() {
return this.trailingSlashMatch;
}
/**
* Whether to apply suffix pattern matching in PatternsRequestCondition.
* <p>By default this is set to 'true'.
* @see #setRegisteredSuffixPatternMatch(boolean)
*/
public void setSuffixPatternMatch(boolean suffixPatternMatch) {
this.suffixPatternMatch = suffixPatternMatch;
}
public boolean useSuffixPatternMatch() {
return this.suffixPatternMatch;
}
/**
* Whether suffix pattern matching should be restricted to registered
* file extensions only. Setting this property also sets
* suffixPatternMatch=true and requires that a
* {@link #setContentNegotiationManager} is also configured in order to
* obtain the registered file extensions.
*/
public void setRegisteredSuffixPatternMatch(boolean registeredSuffixPatternMatch) {
this.registeredSuffixPatternMatch = registeredSuffixPatternMatch;
this.suffixPatternMatch = (registeredSuffixPatternMatch || this.suffixPatternMatch);
}
public boolean useRegisteredSuffixPatternMatch() {
return this.registeredSuffixPatternMatch;
}
/**
* Return the file extensions to use for suffix pattern matching. If
* {@code registeredSuffixPatternMatch=true}, the extensions are obtained
* from the configured {@code contentNegotiationManager}.
*/
public List<String> getFileExtensions() {
if (useRegisteredSuffixPatternMatch() && getContentNegotiationManager() != null) {
return this.contentNegotiationManager.getAllFileExtensions();
}
return null;
}
/**
* Set the ContentNegotiationManager to use for the ProducesRequestCondition.
* <p>By default this is not set.
*/
public void setContentNegotiationManager(ContentNegotiationManager manager) {
this.contentNegotiationManager = manager;
}
public ContentNegotiationManager getContentNegotiationManager() {
return this.contentNegotiationManager;
}
}
}

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

@ -18,7 +18,6 @@ package org.springframework.web.servlet.mvc.method.annotation; @@ -18,7 +18,6 @@ package org.springframework.web.servlet.mvc.method.annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import org.springframework.context.EmbeddedValueResolverAware;
@ -38,14 +37,8 @@ import org.springframework.web.cors.CorsConfiguration; @@ -38,14 +37,8 @@ import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.mvc.condition.AbstractRequestCondition;
import org.springframework.web.servlet.mvc.condition.CompositeRequestCondition;
import org.springframework.web.servlet.mvc.condition.ConsumesRequestCondition;
import org.springframework.web.servlet.mvc.condition.HeadersRequestCondition;
import org.springframework.web.servlet.mvc.condition.NameValueExpression;
import org.springframework.web.servlet.mvc.condition.ParamsRequestCondition;
import org.springframework.web.servlet.mvc.condition.PatternsRequestCondition;
import org.springframework.web.servlet.mvc.condition.ProducesRequestCondition;
import org.springframework.web.servlet.mvc.condition.RequestCondition;
import org.springframework.web.servlet.mvc.condition.RequestMethodsRequestCondition;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping;
@ -69,10 +62,10 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi @@ -69,10 +62,10 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi
private ContentNegotiationManager contentNegotiationManager = new ContentNegotiationManager();
private final List<String> fileExtensions = new ArrayList<String>();
private StringValueResolver embeddedValueResolver;
private RequestMappingInfo.BuilderConfiguration config = new RequestMappingInfo.BuilderConfiguration();
/**
* Whether to use suffix pattern match (".*") when matching patterns to
@ -130,13 +123,17 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi @@ -130,13 +123,17 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi
@Override
public void afterPropertiesSet() {
if (this.useRegisteredSuffixPatternMatch) {
this.fileExtensions.addAll(this.contentNegotiationManager.getAllFileExtensions());
}
super.afterPropertiesSet();
}
this.config = new RequestMappingInfo.BuilderConfiguration();
this.config.setPathHelper(getUrlPathHelper());
this.config.setPathMatcher(getPathMatcher());
this.config.setSuffixPatternMatch(this.useSuffixPatternMatch);
this.config.setTrailingSlashMatch(this.useTrailingSlashMatch);
this.config.setRegisteredSuffixPatternMatch(this.useRegisteredSuffixPatternMatch);
this.config.setContentNegotiationManager(getContentNegotiationManager());
super.afterPropertiesSet();
}
/**
* Whether to use suffix pattern matching.
@ -170,7 +167,7 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi @@ -170,7 +167,7 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi
* Return the file extensions to use for suffix pattern matching.
*/
public List<String> getFileExtensions() {
return this.fileExtensions;
return this.config.getFileExtensions();
}
@ -215,6 +212,7 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi @@ -215,6 +212,7 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi
* @param handlerType the handler type for which to create the condition
* @return the condition, or {@code null}
*/
@SuppressWarnings("unused")
protected RequestCondition<?> getCustomTypeCondition(Class<?> handlerType) {
return null;
}
@ -230,6 +228,7 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi @@ -230,6 +228,7 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi
* @param method the handler method for which to create the condition
* @return the condition, or {@code null}
*/
@SuppressWarnings("unused")
protected RequestCondition<?> getCustomMethodCondition(Method method) {
return null;
}
@ -238,6 +237,7 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi @@ -238,6 +237,7 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi
* Transitional method used to invoke one of two createRequestMappingInfo
* variants one of which is deprecated.
*/
@SuppressWarnings("deprecation")
private RequestMappingInfo createRequestMappingInfo(AnnotatedElement annotatedElement) {
RequestMapping annotation;
AnnotationAttributes attributes;
@ -272,6 +272,7 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi @@ -272,6 +272,7 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi
* {@link #createRequestMappingInfo(AnnotationAttributes, RequestCondition)}.
*/
@Deprecated
@SuppressWarnings("unused")
protected RequestMappingInfo createRequestMappingInfo(RequestMapping annotation,
RequestCondition<?> customCondition) {
@ -287,31 +288,20 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi @@ -287,31 +288,20 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi
protected RequestMappingInfo createRequestMappingInfo(AnnotationAttributes attributes,
RequestCondition<?> customCondition) {
String mappingName = attributes.getString("name");
String[] paths = attributes.getStringArray("path");
paths = ObjectUtils.isEmpty(paths) ? attributes.getStringArray("value") : paths;
PatternsRequestCondition patternsCondition = new PatternsRequestCondition(
resolveEmbeddedValuesInPatterns(paths), getUrlPathHelper(), getPathMatcher(),
this.useSuffixPatternMatch, this.useTrailingSlashMatch, this.fileExtensions);
RequestMethod[] methods = (RequestMethod[]) attributes.get("method");
RequestMethodsRequestCondition methodsCondition = new RequestMethodsRequestCondition(methods);
String[] params = attributes.getStringArray("params");
ParamsRequestCondition paramsCondition = new ParamsRequestCondition(params);
String[] headers = attributes.getStringArray("headers");
String[] consumes = attributes.getStringArray("consumes");
String[] produces = attributes.getStringArray("produces");
HeadersRequestCondition headersCondition = new HeadersRequestCondition(headers);
ConsumesRequestCondition consumesCondition = new ConsumesRequestCondition(consumes, headers);
ProducesRequestCondition producesCondition = new ProducesRequestCondition(produces,
headers, this.contentNegotiationManager);
return new RequestMappingInfo(mappingName, patternsCondition, methodsCondition, paramsCondition,
headersCondition, consumesCondition, producesCondition, customCondition);
paths = resolveEmbeddedValuesInPatterns(paths);
return RequestMappingInfo.paths(paths)
.methods((RequestMethod[]) attributes.get("method"))
.params(attributes.getStringArray("params"))
.headers(attributes.getStringArray("headers"))
.consumes(attributes.getStringArray("consumes"))
.produces(attributes.getStringArray("produces"))
.mappingName(attributes.getString("name"))
.customCondition(customCondition)
.options(this.config)
.build();
}
/**
@ -342,8 +332,8 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi @@ -342,8 +332,8 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi
}
CorsConfiguration config = new CorsConfiguration();
applyAnnotation(config, typeAnnotation);
applyAnnotation(config, methodAnnotation);
updateCorsConfig(config, typeAnnotation);
updateCorsConfig(config, methodAnnotation);
if (CollectionUtils.isEmpty(config.getAllowedMethods())) {
for (RequestMethod allowedMethod : mappingInfo.getMethodsCondition().getMethods()) {
@ -360,7 +350,7 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi @@ -360,7 +350,7 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi
return config;
}
private void applyAnnotation(CorsConfiguration config, CrossOrigin annotation) {
private void updateCorsConfig(CorsConfiguration config, CrossOrigin annotation) {
if (annotation == null) {
return;
}

Loading…
Cancel
Save