Skip to content

Commit 35971f9

Browse files
committed
revised handler method resolution, in particular with respect to generic interfaces (SPR-7355)
1 parent 49a2970 commit 35971f9

File tree

7 files changed

+321
-113
lines changed

7 files changed

+321
-113
lines changed

org.springframework.core/src/main/java/org/springframework/util/ReflectionUtils.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -622,4 +622,16 @@ public boolean matches(Method method) {
622622
}
623623
};
624624

625+
626+
/**
627+
* Pre-built MethodFilter that matches all non-bridge methods
628+
* which are not declared on <code>java.lang.Object</code>.
629+
*/
630+
public static MethodFilter USER_DECLARED_METHODS = new MethodFilter() {
631+
632+
public boolean matches(Method method) {
633+
return (!method.isBridge() && method.getDeclaringClass() != Object.class);
634+
}
635+
};
636+
625637
}

org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/mvc/annotation/AnnotationMethodHandlerAdapter.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -435,6 +435,9 @@ public PortletHandlerMethodResolver(Class<?> handlerType) {
435435

436436
@Override
437437
protected boolean isHandlerMethod(Method method) {
438+
if (this.mappings.containsKey(method)) {
439+
return true;
440+
}
438441
RequestMappingInfo mappingInfo = new RequestMappingInfo();
439442
RequestMapping requestMapping = AnnotationUtils.findAnnotation(method, RequestMapping.class);
440443
ActionMapping actionMapping = AnnotationUtils.findAnnotation(method, ActionMapping.class);
@@ -460,8 +463,11 @@ protected boolean isHandlerMethod(Method method) {
460463
mappingInfo.phase = determineDefaultPhase(method);
461464
}
462465
}
463-
this.mappings.put(method, mappingInfo);
464-
return (mappingInfo.phase != null);
466+
if (mappingInfo.phase != null) {
467+
this.mappings.put(method, mappingInfo);
468+
return true;
469+
}
470+
return false;
465471
}
466472

467473
public Method resolveHandlerMethod(PortletRequest request) throws PortletException {

org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/mvc/annotation/DefaultAnnotationHandlerMapping.java

Lines changed: 61 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2009 the original author or authors.
2+
* Copyright 2002-2010 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -21,6 +21,7 @@
2121
import java.util.Arrays;
2222
import java.util.HashMap;
2323
import java.util.HashSet;
24+
import java.util.LinkedHashSet;
2425
import java.util.Map;
2526
import java.util.Set;
2627
import javax.portlet.ClientDataRequest;
@@ -145,71 +146,76 @@ else if (AnnotationUtils.findAnnotation(handlerType, Controller.class) != null)
145146
* @return <code>true</code> if at least 1 handler method has been registered;
146147
* <code>false</code> otherwise
147148
*/
148-
protected boolean detectHandlerMethods(Class handlerType, final String beanName, final RequestMapping typeMapping) {
149+
protected boolean detectHandlerMethods(Class<?> handlerType, final String beanName, final RequestMapping typeMapping) {
149150
final Set<Boolean> handlersRegistered = new HashSet<Boolean>(1);
150-
ReflectionUtils.doWithMethods(handlerType, new ReflectionUtils.MethodCallback() {
151-
public void doWith(Method method) {
152-
boolean mappingFound = false;
153-
String[] modeKeys = new String[0];
154-
String[] params = new String[0];
155-
String resourceId = null;
156-
String eventName = null;
157-
for (Annotation ann : method.getAnnotations()) {
158-
if (AnnotationUtils.findAnnotation(ann.getClass(), Mapping.class) != null) {
159-
mappingFound = true;
160-
if (ann instanceof RequestMapping) {
161-
RequestMapping rm = (RequestMapping) ann;
162-
modeKeys = rm.value();
163-
params = StringUtils.mergeStringArrays(params, rm.params());
151+
Set<Class<?>> handlerTypes = new LinkedHashSet<Class<?>>();
152+
handlerTypes.add(handlerType);
153+
handlerTypes.addAll(Arrays.asList(handlerType.getInterfaces()));
154+
for (Class<?> currentHandlerType : handlerTypes) {
155+
ReflectionUtils.doWithMethods(currentHandlerType, new ReflectionUtils.MethodCallback() {
156+
public void doWith(Method method) {
157+
boolean mappingFound = false;
158+
String[] modeKeys = new String[0];
159+
String[] params = new String[0];
160+
String resourceId = null;
161+
String eventName = null;
162+
for (Annotation ann : method.getAnnotations()) {
163+
if (AnnotationUtils.findAnnotation(ann.getClass(), Mapping.class) != null) {
164+
mappingFound = true;
165+
if (ann instanceof RequestMapping) {
166+
RequestMapping rm = (RequestMapping) ann;
167+
modeKeys = rm.value();
168+
params = StringUtils.mergeStringArrays(params, rm.params());
169+
}
170+
else if (ann instanceof ResourceMapping) {
171+
ResourceMapping rm = (ResourceMapping) ann;
172+
resourceId = rm.value();
173+
}
174+
else if (ann instanceof EventMapping) {
175+
EventMapping em = (EventMapping) ann;
176+
eventName = em.value();
177+
}
178+
else {
179+
String[] specificParams = (String[]) AnnotationUtils.getValue(ann, "params");
180+
params = StringUtils.mergeStringArrays(params, specificParams);
181+
}
164182
}
165-
else if (ann instanceof ResourceMapping) {
166-
ResourceMapping rm = (ResourceMapping) ann;
167-
resourceId = rm.value();
183+
}
184+
if (mappingFound) {
185+
if (modeKeys.length == 0) {
186+
if (typeMapping != null) {
187+
modeKeys = typeMapping.value();
188+
}
189+
else {
190+
throw new IllegalStateException(
191+
"No portlet mode mappings specified - neither at type nor at method level");
192+
}
168193
}
169-
else if (ann instanceof EventMapping) {
170-
EventMapping em = (EventMapping) ann;
171-
eventName = em.value();
194+
if (typeMapping != null) {
195+
if (!PortletAnnotationMappingUtils.validateModeMapping(modeKeys, typeMapping.value())) {
196+
throw new IllegalStateException("Mode mappings conflict between method and type level: " +
197+
Arrays.asList(modeKeys) + " versus " + Arrays.asList(typeMapping.value()));
198+
}
199+
params = StringUtils.mergeStringArrays(typeMapping.params(), params);
172200
}
173-
else {
174-
String[] specificParams = (String[]) AnnotationUtils.getValue(ann, "params");
175-
params = StringUtils.mergeStringArrays(params, specificParams);
201+
PortletRequestMappingPredicate predicate;
202+
if (resourceId != null) {
203+
predicate = new ResourceMappingPredicate(resourceId);
176204
}
177-
}
178-
}
179-
if (mappingFound) {
180-
if (modeKeys.length == 0) {
181-
if (typeMapping != null) {
182-
modeKeys = typeMapping.value();
205+
else if (eventName != null) {
206+
predicate = new EventMappingPredicate(eventName);
183207
}
184208
else {
185-
throw new IllegalStateException(
186-
"No portlet mode mappings specified - neither at type nor at method level");
209+
predicate = new ParameterMappingPredicate(params);
187210
}
188-
}
189-
if (typeMapping != null) {
190-
if (!PortletAnnotationMappingUtils.validateModeMapping(modeKeys, typeMapping.value())) {
191-
throw new IllegalStateException("Mode mappings conflict between method and type level: " +
192-
Arrays.asList(modeKeys) + " versus " + Arrays.asList(typeMapping.value()));
211+
for (String modeKey : modeKeys) {
212+
registerHandler(new PortletMode(modeKey), beanName, predicate);
213+
handlersRegistered.add(Boolean.TRUE);
193214
}
194-
params = StringUtils.mergeStringArrays(typeMapping.params(), params);
195-
}
196-
PortletRequestMappingPredicate predicate;
197-
if (resourceId != null) {
198-
predicate = new ResourceMappingPredicate(resourceId);
199-
}
200-
else if (eventName != null) {
201-
predicate = new EventMappingPredicate(eventName);
202-
}
203-
else {
204-
predicate = new ParameterMappingPredicate(params);
205-
}
206-
for (String modeKey : modeKeys) {
207-
registerHandler(new PortletMode(modeKey), beanName, predicate);
208-
handlersRegistered.add(Boolean.TRUE);
209215
}
210216
}
211-
}
212-
});
217+
}, ReflectionUtils.USER_DECLARED_METHODS);
218+
}
213219
return !handlersRegistered.isEmpty();
214220
}
215221

org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/annotation/AnnotationMethodHandlerAdapter.java

Lines changed: 28 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import java.util.Arrays;
2828
import java.util.Collections;
2929
import java.util.Comparator;
30+
import java.util.HashMap;
3031
import java.util.LinkedHashMap;
3132
import java.util.LinkedHashSet;
3233
import java.util.List;
@@ -496,18 +497,44 @@ protected HttpOutputMessage createHttpOutputMessage(HttpServletResponse servletR
496497
*/
497498
private class ServletHandlerMethodResolver extends HandlerMethodResolver {
498499

500+
private final Map<Method, RequestMappingInfo> mappings = new HashMap<Method, RequestMappingInfo>();
501+
499502
private ServletHandlerMethodResolver(Class<?> handlerType) {
500503
init(handlerType);
501504
}
502505

506+
@Override
507+
protected boolean isHandlerMethod(Method method) {
508+
if (this.mappings.containsKey(method)) {
509+
return true;
510+
}
511+
RequestMapping mapping = AnnotationUtils.findAnnotation(method, RequestMapping.class);
512+
if (mapping != null) {
513+
RequestMappingInfo mappingInfo = new RequestMappingInfo();
514+
mappingInfo.patterns = mapping.value();
515+
if (!hasTypeLevelMapping() || !Arrays.equals(mapping.method(), getTypeLevelMapping().method())) {
516+
mappingInfo.methods = mapping.method();
517+
}
518+
if (!hasTypeLevelMapping() || !Arrays.equals(mapping.params(), getTypeLevelMapping().params())) {
519+
mappingInfo.params = mapping.params();
520+
}
521+
if (!hasTypeLevelMapping() || !Arrays.equals(mapping.headers(), getTypeLevelMapping().headers())) {
522+
mappingInfo.headers = mapping.headers();
523+
}
524+
this.mappings.put(method, mappingInfo);
525+
return true;
526+
}
527+
return false;
528+
}
529+
503530
public Method resolveHandlerMethod(HttpServletRequest request) throws ServletException {
504531
String lookupPath = urlPathHelper.getLookupPathForRequest(request);
505532
Comparator<String> pathComparator = pathMatcher.getPatternComparator(lookupPath);
506533
Map<RequestMappingInfo, Method> targetHandlerMethods = new LinkedHashMap<RequestMappingInfo, Method>();
507534
Set<String> allowedMethods = new LinkedHashSet<String>(7);
508535
String resolvedMethodName = null;
509536
for (Method handlerMethod : getHandlerMethods()) {
510-
RequestMappingInfo mappingInfo = createRequestMappingInfo(handlerMethod);
537+
RequestMappingInfo mappingInfo = this.mappings.get(handlerMethod);
511538
boolean match = false;
512539
if (mappingInfo.hasPatterns()) {
513540
List<String> matchingPatterns = new ArrayList<String>(mappingInfo.patterns.length);
@@ -599,22 +626,6 @@ public Method resolveHandlerMethod(HttpServletRequest request) throws ServletExc
599626
}
600627
}
601628

602-
private RequestMappingInfo createRequestMappingInfo(Method handlerMethod) {
603-
RequestMappingInfo mappingInfo = new RequestMappingInfo();
604-
RequestMapping mapping = AnnotationUtils.findAnnotation(handlerMethod, RequestMapping.class);
605-
mappingInfo.patterns = mapping.value();
606-
if (!hasTypeLevelMapping() || !Arrays.equals(mapping.method(), getTypeLevelMapping().method())) {
607-
mappingInfo.methods = mapping.method();
608-
}
609-
if (!hasTypeLevelMapping() || !Arrays.equals(mapping.params(), getTypeLevelMapping().params())) {
610-
mappingInfo.params = mapping.params();
611-
}
612-
if (!hasTypeLevelMapping() || !Arrays.equals(mapping.headers(), getTypeLevelMapping().headers())) {
613-
mappingInfo.headers = mapping.headers();
614-
}
615-
return mappingInfo;
616-
}
617-
618629
/**
619630
* Determines the combined pattern for the given methodLevelPattern and path.
620631
* <p>Uses the following algorithm: <ol>

org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/annotation/DefaultAnnotationHandlerMapping.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
package org.springframework.web.servlet.mvc.annotation;
1818

1919
import java.lang.reflect.Method;
20-
import java.lang.reflect.Proxy;
20+
import java.util.Arrays;
2121
import java.util.HashMap;
2222
import java.util.LinkedHashSet;
2323
import java.util.Map;
@@ -165,8 +165,9 @@ protected String[] determineUrlsForHandlerMethods(Class<?> handlerType, final bo
165165
}
166166

167167
final Set<String> urls = new LinkedHashSet<String>();
168-
Class<?>[] handlerTypes =
169-
Proxy.isProxyClass(handlerType) ? handlerType.getInterfaces() : new Class<?>[]{handlerType};
168+
Set<Class<?>> handlerTypes = new LinkedHashSet<Class<?>>();
169+
handlerTypes.add(handlerType);
170+
handlerTypes.addAll(Arrays.asList(handlerType.getInterfaces()));
170171
for (Class<?> currentHandlerType : handlerTypes) {
171172
ReflectionUtils.doWithMethods(currentHandlerType, new ReflectionUtils.MethodCallback() {
172173
public void doWith(Method method) {
@@ -187,7 +188,7 @@ else if (hasTypeLevelMapping) {
187188
}
188189
}
189190
}
190-
}, ReflectionUtils.NON_BRIDGED_METHODS);
191+
}, ReflectionUtils.USER_DECLARED_METHODS);
191192
}
192193
return StringUtils.toStringArray(urls);
193194
}

0 commit comments

Comments
 (0)