Support @RequestMapping as meta-annotation

Issue: SPR-12296
This commit is contained in:
Rossen Stoyanchev
2015-02-17 17:00:21 -05:00
parent 60b19c784d
commit 8376e1eca1
5 changed files with 213 additions and 56 deletions

View File

@@ -40,7 +40,8 @@ import org.springframework.cglib.proxy.MethodProxy;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.MethodParameter;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.objenesis.Objenesis;
import org.springframework.objenesis.SpringObjenesis;
import org.springframework.util.AntPathMatcher;
@@ -377,16 +378,40 @@ public class MvcUriComponentsBuilder {
private static String getTypeRequestMapping(Class<?> controllerType) {
Assert.notNull(controllerType, "'controllerType' must not be null");
RequestMapping annot = AnnotationUtils.findAnnotation(controllerType, RequestMapping.class);
if (annot == null || ObjectUtils.isEmpty(annot.value()) || StringUtils.isEmpty(annot.value()[0])) {
String annotType = RequestMapping.class.getName();
AnnotationAttributes attrs = AnnotatedElementUtils.getAnnotationAttributes(controllerType, annotType);
if (attrs == null) {
return "/";
}
if (annot.value().length > 1 && logger.isWarnEnabled()) {
String[] paths = attrs.getStringArray("path");
paths = ObjectUtils.isEmpty(paths) ? attrs.getStringArray("value") : paths;
if (ObjectUtils.isEmpty(paths) || StringUtils.isEmpty(paths[0])) {
return "/";
}
if (paths.length > 1 && logger.isWarnEnabled()) {
logger.warn("Multiple paths on controller " + controllerType.getName() + ", using first one");
}
return annot.value()[0];
return paths[0];
}
private static String getMethodRequestMapping(Method method) {
String annotType = RequestMapping.class.getName();
AnnotationAttributes attrs = AnnotatedElementUtils.getAnnotationAttributes(method, annotType);
if (attrs == null) {
throw new IllegalArgumentException("No @RequestMapping on: " + method.toGenericString());
}
String[] paths = attrs.getStringArray("path");
paths = ObjectUtils.isEmpty(paths) ? attrs.getStringArray("value") : paths;
if (ObjectUtils.isEmpty(paths) || StringUtils.isEmpty(paths[0])) {
return "/";
}
if (paths.length > 1 && logger.isWarnEnabled()) {
logger.warn("Multiple paths on method " + method.toGenericString() + ", using first one");
}
return paths[0];
}
private static Method getMethod(Class<?> controllerType, String methodName, Object... args) {
Method match = null;
for (Method method : controllerType.getDeclaredMethods()) {
@@ -405,20 +430,6 @@ public class MvcUriComponentsBuilder {
return match;
}
private static String getMethodRequestMapping(Method method) {
RequestMapping annot = AnnotationUtils.findAnnotation(method, RequestMapping.class);
if (annot == null) {
throw new IllegalArgumentException("No @RequestMapping on: " + method.toGenericString());
}
if (ObjectUtils.isEmpty(annot.value()) || StringUtils.isEmpty(annot.value()[0])) {
return "/";
}
if (annot.value().length > 1 && logger.isWarnEnabled()) {
logger.warn("Multiple paths on method " + method.toGenericString() + ", using first one");
}
return annot.value()[0];
}
private static UriComponents applyContributors(UriComponentsBuilder builder, Method method, Object... args) {
CompositeUriComponentsContributor contributor = getConfiguredUriComponentsContributor();
if (contributor == null) {

View File

@@ -16,15 +16,19 @@
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;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Controller;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringValueResolver;
import org.springframework.web.accept.ContentNegotiationManager;
import org.springframework.web.bind.annotation.CrossOrigin;
@@ -190,15 +194,11 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi
*/
@Override
protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
RequestMappingInfo info = null;
RequestMapping methodAnnotation = AnnotationUtils.findAnnotation(method, RequestMapping.class);
if (methodAnnotation != null) {
RequestCondition<?> methodCondition = getCustomMethodCondition(method);
info = createRequestMappingInfo(methodAnnotation, methodCondition);
RequestMapping typeAnnotation = AnnotationUtils.findAnnotation(handlerType, RequestMapping.class);
if (typeAnnotation != null) {
RequestCondition<?> typeCondition = getCustomTypeCondition(handlerType);
info = createRequestMappingInfo(typeAnnotation, typeCondition).combine(info);
RequestMappingInfo info = createRequestMappingInfo(method);
if (info != null) {
RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);
if (typeInfo != null) {
info = typeInfo.combine(info);
}
}
return info;
@@ -235,20 +235,83 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi
}
/**
* Created a RequestMappingInfo from a RequestMapping annotation.
* Transitional method used to invoke one of two createRequestMappingInfo
* variants one of which is deprecated.
*/
protected RequestMappingInfo createRequestMappingInfo(RequestMapping annotation, RequestCondition<?> customCondition) {
String[] patterns = resolveEmbeddedValuesInPatterns(annotation.value());
return new RequestMappingInfo(
annotation.name(),
new PatternsRequestCondition(patterns, getUrlPathHelper(), getPathMatcher(),
this.useSuffixPatternMatch, this.useTrailingSlashMatch, this.fileExtensions),
new RequestMethodsRequestCondition(annotation.method()),
new ParamsRequestCondition(annotation.params()),
new HeadersRequestCondition(annotation.headers()),
new ConsumesRequestCondition(annotation.consumes(), annotation.headers()),
new ProducesRequestCondition(annotation.produces(), annotation.headers(), this.contentNegotiationManager),
customCondition);
private RequestMappingInfo createRequestMappingInfo(AnnotatedElement annotatedElement) {
RequestMapping annotation;
AnnotationAttributes attributes;
RequestCondition<?> customCondition;
String annotationType = RequestMapping.class.getName();
if (annotatedElement instanceof Class<?>) {
Class<?> type = (Class<?>) annotatedElement;
annotation = AnnotationUtils.findAnnotation(type, RequestMapping.class);
attributes = AnnotatedElementUtils.getAnnotationAttributes(type, annotationType);
customCondition = getCustomTypeCondition(type);
}
else {
Method method = (Method) annotatedElement;
annotation = AnnotationUtils.findAnnotation(method, RequestMapping.class);
attributes = AnnotatedElementUtils.getAnnotationAttributes(method, annotationType);
customCondition = getCustomMethodCondition(method);
}
RequestMappingInfo info = null;
if (annotation != null) {
info = createRequestMappingInfo(annotation, customCondition);
if (info == null) {
info = createRequestMappingInfo(attributes, customCondition);
}
}
return info;
}
/**
* Create a RequestMappingInfo from a RequestMapping annotation.
* @deprecated as of 4.2 after the introduction of support for
* {@code @RequestMapping} as meta-annotation. Please use
* {@link #createRequestMappingInfo(AnnotationAttributes, RequestCondition)}.
*/
@Deprecated
protected RequestMappingInfo createRequestMappingInfo(RequestMapping annotation,
RequestCondition<?> customCondition) {
return null;
}
/**
* Create a RequestMappingInfo from the attributes of an
* {@code @RequestMapping} annotation or a meta-annotation, i.e. a custom
* annotation annotated with {@code @RequestMapping}.
* @since 4.2
*/
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);
}
/**
@@ -320,7 +383,8 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi
config.setAllowCredentials(false);
}
else if (!annotation.allowCredentials().isEmpty()) {
throw new IllegalStateException("AllowCredentials value must be \"true\", \"false\" or \"\" (empty string), current value is " + annotation.allowCredentials());
throw new IllegalStateException("AllowCredentials value must be \"true\", \"false\" " +
"or \"\" (empty string), current value is " + annotation.allowCredentials());
}
if (annotation.maxAge() != -1 && config.getMaxAge() == null) {
config.setMaxAge(annotation.maxAge());