Consistent method selection for listeners and endpoint mappings

Issue: SPR-13654
This commit is contained in:
Juergen Hoeller
2015-11-09 14:57:45 +01:00
parent 5b06150096
commit bc7bcab578
10 changed files with 264 additions and 160 deletions

View File

@@ -0,0 +1,157 @@
/*
* Copyright 2002-2015 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.core;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;
/**
* Defines the algorithm for searching for metadata-associated methods exhaustively
* including interfaces and parent classes while also dealing with parameterized methods
* as well as common scenarios encountered with interface and class-based proxies.
*
* <p>Typically, but not necessarily, used for finding annotated handler methods.
*
* @author Juergen Hoeller
* @author Rossen Stoyanchev
* @since 4.2.3
*/
public abstract class MethodIntrospector {
/**
* Select methods on the given target type based on the lookup of associated metadata.
* <p>Callers define methods of interest through the {@link MetadataLookup} parameter,
* allowing to collect the associated metadata into the result map.
* @param targetType the target type to search methods on
* @param metadataLookup a {@link MetadataLookup} callback to inspect methods of interest,
* returning non-null metadata to be associated with a given method if there is a match,
* or {@code null} for no match
* @return the selected methods associated with their metadata (in the order of retrieval),
* or an empty map in case of no match
*/
public static <T> Map<Method, T> selectMethods(Class<?> targetType, final MetadataLookup<T> metadataLookup) {
final Map<Method, T> methodMap = new LinkedHashMap<Method, T>();
Set<Class<?>> handlerTypes = new LinkedHashSet<Class<?>>();
Class<?> specificHandlerType = null;
if (!Proxy.isProxyClass(targetType)) {
handlerTypes.add(targetType);
specificHandlerType = targetType;
}
handlerTypes.addAll(Arrays.asList(targetType.getInterfaces()));
for (Class<?> currentHandlerType : handlerTypes) {
final Class<?> targetClass = (specificHandlerType != null ? specificHandlerType : currentHandlerType);
ReflectionUtils.doWithMethods(currentHandlerType, new ReflectionUtils.MethodCallback() {
@Override
public void doWith(Method method) {
Method specificMethod = ClassUtils.getMostSpecificMethod(method, targetClass);
T result = metadataLookup.inspect(specificMethod);
if (result != null) {
Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);
if (bridgedMethod == specificMethod || metadataLookup.inspect(bridgedMethod) == null) {
methodMap.put(specificMethod, result);
}
}
}
}, ReflectionUtils.USER_DECLARED_METHODS);
}
return methodMap;
}
/**
* Select methods on the given target type based on a filter.
* <p>Callers define methods of interest through the
* {@link ReflectionUtils.MethodFilter} parameter.
* @param targetType the target type to search methods on
* @param methodFilter a {@link ReflectionUtils.MethodFilter} to help
* recognize handler methods of interest
* @return the selected methods, or an empty set in case of no match
*/
public static Set<Method> selectMethods(Class<?> targetType, final ReflectionUtils.MethodFilter methodFilter) {
return selectMethods(targetType, new MetadataLookup<Boolean>() {
@Override
public Boolean inspect(Method method) {
return (methodFilter.matches(method) ? Boolean.TRUE : null);
}
}).keySet();
}
/**
* Select an invocable method on the target type: either the given method itself
* if actually exposed on the target type, or otherwise a corresponding method
* on one of the target type's interfaces or on the target type itself.
* <p>Matches on user-declared interfaces will be preferred since they are likely
* to contain relevant metadata that corresponds to the method on the target class.
* @param method the method to check
* @param targetType the target type to search methods on
* (typically an interface-based JDK proxy)
* @return a corresponding invocable method on the target type
*/
public static Method selectInvocableMethod(Method method, Class<?> targetType) {
if (method.getDeclaringClass().isAssignableFrom(targetType)) {
return method;
}
try {
for (Class<?> ifc : targetType.getInterfaces()) {
try {
return ifc.getMethod(method.getName(), method.getParameterTypes());
}
catch (NoSuchMethodException ex) {
// Alright, not on this interface then...
}
}
// A final desperate attempt on the proxy class itself...
return targetType.getMethod(method.getName(), method.getParameterTypes());
}
catch (NoSuchMethodException ex) {
throw new IllegalStateException(String.format(
"Need to invoke method '%s' declared on target class '%s', " +
"but not found in any interface(s) of the exposed proxy type. " +
"Either pull the method up to an interface or switch to CGLIB " +
"proxies by enforcing proxy-target-class mode in your configuration.",
method.getName(), method.getDeclaringClass().getSimpleName()));
}
}
/**
* A callback interface for metadata lookup on a given method.
* @param <T> the type of metadata returned
*/
public interface MetadataLookup<T> {
/**
* Perform a lookup on the given method and return associated metadata, if any.
* @param method the method to inspect
* @return non-null metadata to be associated with a method if there is a match,
* or {@code null} for no match
*/
T inspect(Method method);
}
}

View File

@@ -17,14 +17,9 @@
package org.springframework.messaging.handler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.Set;
import org.springframework.core.BridgeMethodResolver;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;
import org.springframework.core.MethodIntrospector;
import org.springframework.util.ReflectionUtils.MethodFilter;
/**
@@ -33,7 +28,9 @@ import org.springframework.util.ReflectionUtils.MethodFilter;
*
* @author Rossen Stoyanchev
* @since 4.0
* @deprecated as of Spring 4.2.3, in favor of the generalized and refined {@link MethodIntrospector}
*/
@Deprecated
public abstract class HandlerMethodSelector {
/**
@@ -42,31 +39,10 @@ public abstract class HandlerMethodSelector {
* @param handlerType the handler type to search handler methods on
* @param handlerMethodFilter a {@link MethodFilter} to help recognize handler methods of interest
* @return the selected methods, or an empty set
* @see MethodIntrospector#selectMethods(Class, MethodFilter)
*/
public static Set<Method> selectMethods(final Class<?> handlerType, final MethodFilter handlerMethodFilter) {
final Set<Method> handlerMethods = new LinkedHashSet<Method>();
Set<Class<?>> handlerTypes = new LinkedHashSet<Class<?>>();
Class<?> specificHandlerType = null;
if (!Proxy.isProxyClass(handlerType)) {
handlerTypes.add(handlerType);
specificHandlerType = handlerType;
}
handlerTypes.addAll(Arrays.asList(handlerType.getInterfaces()));
for (Class<?> currentHandlerType : handlerTypes) {
final Class<?> targetClass = (specificHandlerType != null ? specificHandlerType : currentHandlerType);
ReflectionUtils.doWithMethods(currentHandlerType, new ReflectionUtils.MethodCallback() {
@Override
public void doWith(Method method) {
Method specificMethod = ClassUtils.getMostSpecificMethod(method, targetClass);
Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);
if (handlerMethodFilter.matches(specificMethod) &&
(bridgedMethod == specificMethod || !handlerMethodFilter.matches(bridgedMethod))) {
handlerMethods.add(specificMethod);
}
}
}, ReflectionUtils.USER_DECLARED_METHODS);
}
return handlerMethods;
public static Set<Method> selectMethods(Class<?> handlerType, MethodFilter handlerMethodFilter) {
return MethodIntrospector.selectMethods(handlerType, handlerMethodFilter);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2015 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.
@@ -23,8 +23,8 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.core.MethodIntrospector;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.messaging.handler.HandlerMethodSelector;
import org.springframework.messaging.handler.annotation.MessageExceptionHandler;
import org.springframework.messaging.handler.invocation.AbstractExceptionHandlerMethodResolver;
import org.springframework.util.ReflectionUtils.MethodFilter;
@@ -49,32 +49,39 @@ public class AnnotationExceptionHandlerMethodResolver extends AbstractExceptionH
}
private static Map<Class<? extends Throwable>, Method> initExceptionMappings(Class<?> handlerType) {
Map<Method, MessageExceptionHandler> methods = MethodIntrospector.selectMethods(handlerType,
new MethodIntrospector.MetadataLookup<MessageExceptionHandler>() {
@Override
public MessageExceptionHandler inspect(Method method) {
return AnnotationUtils.findAnnotation(method, MessageExceptionHandler.class);
}
});
Map<Class<? extends Throwable>, Method> result = new HashMap<Class<? extends Throwable>, Method>();
for (Method method : HandlerMethodSelector.selectMethods(handlerType, EXCEPTION_HANDLER_METHOD_FILTER)) {
for (Class<? extends Throwable> exceptionType : getMappedExceptions(method)) {
for (Map.Entry<Method, MessageExceptionHandler> entry : methods.entrySet()) {
Method method = entry.getKey();
List<Class<? extends Throwable>> exceptionTypes = new ArrayList<Class<? extends Throwable>>();
exceptionTypes.addAll(Arrays.asList(entry.getValue().value()));
if (exceptionTypes.isEmpty()) {
exceptionTypes.addAll(getExceptionsFromMethodSignature(method));
}
for (Class<? extends Throwable> exceptionType : exceptionTypes) {
Method oldMethod = result.put(exceptionType, method);
if (oldMethod != null && !oldMethod.equals(method)) {
throw new IllegalStateException(
"Ambiguous @ExceptionHandler method mapped for [" + exceptionType + "]: {" +
oldMethod + ", " + method + "}.");
throw new IllegalStateException("Ambiguous @ExceptionHandler method mapped for [" +
exceptionType + "]: {" + oldMethod + ", " + method + "}");
}
}
}
return result;
}
private static List<Class<? extends Throwable>> getMappedExceptions(Method method) {
List<Class<? extends Throwable>> result = new ArrayList<Class<? extends Throwable>>();
MessageExceptionHandler annot = AnnotationUtils.findAnnotation(method, MessageExceptionHandler.class);
result.addAll(Arrays.asList(annot.value()));
if (result.isEmpty()) {
result.addAll(getExceptionsFromMethodSignature(method));
}
return result;
}
/** A filter for selecting annotated exception handling methods. */
/**
* A filter for selecting annotated exception handling methods.
* @deprecated as of Spring 4.2.3, since it isn't used anymore
*/
@Deprecated
public final static MethodFilter EXCEPTION_HANDLER_METHOD_FILTER = new MethodFilter() {
@Override

View File

@@ -34,20 +34,20 @@ import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.MethodParameter;
import org.springframework.core.MethodIntrospector;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageHandler;
import org.springframework.messaging.MessagingException;
import org.springframework.messaging.handler.DestinationPatternsMessageCondition;
import org.springframework.messaging.handler.HandlerMethod;
import org.springframework.messaging.handler.HandlerMethodSelector;
import org.springframework.messaging.handler.MessagingAdviceBean;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.messaging.support.MessageHeaderAccessor;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.concurrent.ListenableFuture;
import org.springframework.util.concurrent.ListenableFutureCallback;
@@ -60,6 +60,7 @@ import org.springframework.util.concurrent.ListenableFutureCallback;
* exceptions raised during message handling.
*
* @author Rossen Stoyanchev
* @author Juergen Hoeller
* @since 4.0
* @param <T> the type of the Object that contains information mapping a
* {@link org.springframework.messaging.handler.HandlerMethod} to incoming messages
@@ -250,22 +251,24 @@ public abstract class AbstractMethodMessageHandler<T>
* so register it with the extracted mapping information.
* @param handler the handler to check, either an instance of a Spring bean name
*/
protected final void detectHandlerMethods(Object handler) {
protected final void detectHandlerMethods(final Object handler) {
Class<?> handlerType = (handler instanceof String ?
this.applicationContext.getType((String) handler) : handler.getClass());
final Class<?> userType = ClassUtils.getUserClass(handlerType);
Set<Method> methods = HandlerMethodSelector.selectMethods(userType, new ReflectionUtils.MethodFilter() {
@Override
public boolean matches(Method method) {
return getMappingForMethod(method, userType) != null;
}
});
Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
new MethodIntrospector.MetadataLookup<T>() {
@Override
public T inspect(Method method) {
return getMappingForMethod(method, userType);
}
});
for (Method method : methods) {
T mapping = getMappingForMethod(method, userType);
registerHandlerMethod(handler, method, mapping);
if (logger.isDebugEnabled()) {
logger.debug(methods.size() + " message handler methods found on " + userType + ": " + methods);
}
for (Map.Entry<Method, T> entry : methods.entrySet()) {
registerHandlerMethod(handler, entry.getKey(), entry.getValue());
}
}
@@ -277,7 +280,6 @@ public abstract class AbstractMethodMessageHandler<T>
*/
protected abstract T getMappingForMethod(Method method, Class<?> handlerType);
/**
* Register a handler method and its unique mapping.
* @param handler the bean name of the handler or the handler instance
@@ -287,6 +289,7 @@ public abstract class AbstractMethodMessageHandler<T>
* under the same mapping
*/
protected void registerHandlerMethod(Object handler, Method method, T mapping) {
Assert.notNull(mapping, "Mapping must bot be null");
HandlerMethod newHandlerMethod = createHandlerMethod(handler, method);
HandlerMethod oldHandlerMethod = this.handlerMethods.get(mapping);

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2014 the original author or authors.
* Copyright 2002-2015 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.
@@ -17,14 +17,9 @@
package org.springframework.web.method;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.Set;
import org.springframework.core.BridgeMethodResolver;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;
import org.springframework.core.MethodIntrospector;
import org.springframework.util.ReflectionUtils.MethodFilter;
/**
@@ -33,7 +28,9 @@ import org.springframework.util.ReflectionUtils.MethodFilter;
*
* @author Rossen Stoyanchev
* @since 3.1
* @deprecated as of Spring 4.2.3, in favor of the generalized and refined {@link MethodIntrospector}
*/
@Deprecated
public abstract class HandlerMethodSelector {
/**
@@ -42,31 +39,10 @@ public abstract class HandlerMethodSelector {
* @param handlerType the handler type to search handler methods on
* @param handlerMethodFilter a {@link MethodFilter} to help recognize handler methods of interest
* @return the selected methods, or an empty set
* @see MethodIntrospector#selectMethods(Class, MethodFilter)
*/
public static Set<Method> selectMethods(final Class<?> handlerType, final MethodFilter handlerMethodFilter) {
final Set<Method> handlerMethods = new LinkedHashSet<Method>();
Set<Class<?>> handlerTypes = new LinkedHashSet<Class<?>>();
Class<?> specificHandlerType = null;
if (!Proxy.isProxyClass(handlerType)) {
handlerTypes.add(handlerType);
specificHandlerType = handlerType;
}
handlerTypes.addAll(Arrays.asList(handlerType.getInterfaces()));
for (Class<?> currentHandlerType : handlerTypes) {
final Class<?> targetClass = (specificHandlerType != null ? specificHandlerType : currentHandlerType);
ReflectionUtils.doWithMethods(currentHandlerType, new ReflectionUtils.MethodCallback() {
@Override
public void doWith(Method method) {
Method specificMethod = ClassUtils.getMostSpecificMethod(method, targetClass);
Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);
if (handlerMethodFilter.matches(specificMethod) &&
(bridgedMethod == specificMethod || !handlerMethodFilter.matches(bridgedMethod))) {
handlerMethods.add(specificMethod);
}
}
}, ReflectionUtils.USER_DECLARED_METHODS);
}
return handlerMethods;
public static Set<Method> selectMethods(Class<?> handlerType, MethodFilter handlerMethodFilter) {
return MethodIntrospector.selectMethods(handlerType, handlerMethodFilter);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2014 the original author or authors.
* Copyright 2002-2015 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.
@@ -25,12 +25,12 @@ import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.springframework.core.ExceptionDepthComparator;
import org.springframework.core.MethodIntrospector;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils.MethodFilter;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.method.HandlerMethodSelector;
/**
* Discovers {@linkplain ExceptionHandler @ExceptionHandler} methods in a given class,
@@ -70,7 +70,7 @@ public class ExceptionHandlerMethodResolver {
* @param handlerType the type to introspect
*/
public ExceptionHandlerMethodResolver(Class<?> handlerType) {
for (Method method : HandlerMethodSelector.selectMethods(handlerType, EXCEPTION_HANDLER_METHODS)) {
for (Method method : MethodIntrospector.selectMethods(handlerType, EXCEPTION_HANDLER_METHODS)) {
for (Class<? extends Throwable> exceptionType : detectExceptionMappings(method)) {
addExceptionMapping(exceptionType, method);
}
@@ -105,9 +105,8 @@ public class ExceptionHandlerMethodResolver {
private void addExceptionMapping(Class<? extends Throwable> exceptionType, Method method) {
Method oldMethod = this.mappedMethods.put(exceptionType, method);
if (oldMethod != null && !oldMethod.equals(method)) {
throw new IllegalStateException(
"Ambiguous @ExceptionHandler method mapped for [" + exceptionType + "]: {" +
oldMethod + ", " + method + "}.");
throw new IllegalStateException("Ambiguous @ExceptionHandler method mapped for [" +
exceptionType + "]: {" + oldMethod + ", " + method + "}");
}
}

View File

@@ -28,6 +28,7 @@ import org.apache.commons.logging.LogFactory;
import org.junit.Before;
import org.junit.Test;
import org.springframework.core.MethodIntrospector;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.mock.web.test.MockHttpServletRequest;
import org.springframework.mock.web.test.MockHttpServletResponse;
@@ -42,7 +43,6 @@ import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.method.HandlerMethodSelector;
import org.springframework.web.method.support.HandlerMethodArgumentResolverComposite;
import org.springframework.web.method.support.InvocableHandlerMethod;
import org.springframework.web.method.support.ModelAndViewContainer;
@@ -119,7 +119,7 @@ public class ModelFactoryOrderingTests {
WebDataBinderFactory dataBinderFactory = new DefaultDataBinderFactory(null);
Class<?> type = controller.getClass();
Set<Method> methods = HandlerMethodSelector.selectMethods(type, METHOD_FILTER);
Set<Method> methods = MethodIntrospector.selectMethods(type, METHOD_FILTER);
List<InvocableHandlerMethod> modelMethods = new ArrayList<InvocableHandlerMethod>();
for (Method method : methods) {
InvocableHandlerMethod modelMethod = new InvocableHandlerMethod(controller, method);

View File

@@ -22,7 +22,6 @@ import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@@ -34,15 +33,14 @@ import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.core.MethodIntrospector;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.util.ReflectionUtils.MethodFilter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsUtils;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.method.HandlerMethodSelector;
import org.springframework.web.servlet.HandlerMapping;
/**
@@ -197,29 +195,23 @@ public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMap
* @param handler the bean name of a handler or a handler instance
*/
protected void detectHandlerMethods(final Object handler) {
Class<?> handlerType =
(handler instanceof String ? getApplicationContext().getType((String) handler) : handler.getClass());
// Avoid repeated calls to getMappingForMethod which would rebuild RequestMappingInfo instances
final Map<Method, T> mappings = new IdentityHashMap<Method, T>();
Class<?> handlerType = (handler instanceof String ?
getApplicationContext().getType((String) handler) : handler.getClass());
final Class<?> userType = ClassUtils.getUserClass(handlerType);
Set<Method> methods = HandlerMethodSelector.selectMethods(userType, new MethodFilter() {
@Override
public boolean matches(Method method) {
T mapping = getMappingForMethod(method, userType);
if (mapping != null) {
mappings.put(method, mapping);
return true;
}
else {
return false;
}
}
});
Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
new MethodIntrospector.MetadataLookup<T>() {
@Override
public T inspect(Method method) {
return getMappingForMethod(method, userType);
}
});
for (Method method : methods) {
registerHandlerMethod(handler, method, mappings.get(method));
if (logger.isDebugEnabled()) {
logger.debug(methods.size() + " request handler methods found on " + userType + ": " + methods);
}
for (Map.Entry<Method, T> entry : methods.entrySet()) {
registerHandlerMethod(handler, entry.getKey(), entry.getValue());
}
}
@@ -241,12 +233,7 @@ public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMap
* @param mapping the mapping conditions associated with the handler method
* @throws IllegalStateException if another method was already registered
* under the same mapping
* @deprecated as of 4.2. You can now invoke the public methods
* {@link #registerMapping(Object, Object, Method)} and
* {@link #unregisterMapping(Object)} during initialization or at runtime,
* i.e. after initialization is complete.
*/
@Deprecated
protected void registerHandlerMethod(Object handler, Method method, T mapping) {
this.mappingRegistry.register(mapping, handler, method);
}
@@ -443,7 +430,7 @@ public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMap
}
else {
CorsConfiguration corsConfigFromMethod = this.mappingRegistry.getCorsConfiguration(handlerMethod);
corsConfig = (corsConfig == null ? corsConfigFromMethod : corsConfig.combine(corsConfigFromMethod));
corsConfig = (corsConfig != null ? corsConfig.combine(corsConfigFromMethod) : corsConfigFromMethod);
}
}
return corsConfig;
@@ -470,10 +457,8 @@ public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMap
private final Map<HandlerMethod, CorsConfiguration> corsLookup =
new ConcurrentHashMap<HandlerMethod, CorsConfiguration>();
private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
/**
* Return all mappings and handler methods. Not thread-safe.
* @see #acquireReadLock()
@@ -521,7 +506,6 @@ public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMap
public void register(T mapping, Object handler, Method method) {
this.readWriteLock.writeLock().lock();
try {
HandlerMethod handlerMethod = createHandlerMethod(handler, method);
@@ -560,8 +544,8 @@ public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMap
if (handlerMethod != null && !handlerMethod.equals(newHandlerMethod)) {
throw new IllegalStateException(
"Ambiguous mapping. Cannot map '" + newHandlerMethod.getBean() + "' method \n" +
newHandlerMethod + "\nto " + mapping + ": There is already '" +
handlerMethod.getBean() + "' bean method\n" + handlerMethod + " mapped.");
newHandlerMethod + "\nto " + mapping + ": There is already '" +
handlerMethod.getBean() + "' bean method\n" + handlerMethod + " mapped.");
}
}
@@ -576,9 +560,10 @@ public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMap
}
private void addMappingName(String name, HandlerMethod handlerMethod) {
List<HandlerMethod> oldList = this.nameLookup.containsKey(name) ?
this.nameLookup.get(name) : Collections.<HandlerMethod>emptyList();
List<HandlerMethod> oldList = this.nameLookup.get(name);
if (oldList == null) {
oldList = Collections.<HandlerMethod>emptyList();
}
for (HandlerMethod current : oldList) {
if (handlerMethod.equals(current)) {
@@ -587,7 +572,7 @@ public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMap
}
if (logger.isTraceEnabled()) {
logger.trace("Mapping name=" + name);
logger.trace("Mapping name '" + name + "'");
}
List<HandlerMethod> newList = new ArrayList<HandlerMethod>(oldList.size() + 1);
@@ -597,7 +582,7 @@ public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMap
if (newList.size() > 1) {
if (logger.isTraceEnabled()) {
logger.trace("Mapping name clash for handlerMethods=" + newList +
logger.trace("Mapping name clash for handlerMethods " + newList +
". Consider assigning explicit names.");
}
}
@@ -654,9 +639,9 @@ public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMap
}
this.nameLookup.put(name, newList);
}
}
private static class MappingRegistration<T> {
private final T mapping;
@@ -667,20 +652,15 @@ public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMap
private final String mappingName;
public MappingRegistration(T mapping, HandlerMethod handlerMethod,
List<String> directUrls, String mappingName) {
public MappingRegistration(T mapping, HandlerMethod handlerMethod, List<String> directUrls, String mappingName) {
Assert.notNull(mapping);
Assert.notNull(handlerMethod);
this.mapping = mapping;
this.handlerMethod = handlerMethod;
this.directUrls = (directUrls != null ? directUrls : Collections.<String>emptyList());
this.mappingName = mappingName;
}
public T getMapping() {
return this.mapping;
}

View File

@@ -39,6 +39,7 @@ import org.springframework.cglib.proxy.Factory;
import org.springframework.cglib.proxy.MethodProxy;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.MethodParameter;
import org.springframework.core.MethodIntrospector;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.core.annotation.SynthesizingMethodParameter;
@@ -57,7 +58,6 @@ import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.method.HandlerMethodSelector;
import org.springframework.web.method.annotation.RequestParamMethodArgumentResolver;
import org.springframework.web.method.support.CompositeUriComponentsContributor;
import org.springframework.web.servlet.DispatcherServlet;
@@ -454,7 +454,7 @@ public class MvcUriComponentsBuilder {
return (name.equals(methodName) && argLength == args.length);
}
};
Set<Method> methods = HandlerMethodSelector.selectMethods(controllerType, selector);
Set<Method> methods = MethodIntrospector.selectMethods(controllerType, selector);
if (methods.size() == 1) {
return methods.iterator().next();
}

View File

@@ -35,6 +35,7 @@ import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.MethodIntrospector;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.core.annotation.AnnotationUtils;
@@ -70,7 +71,6 @@ import org.springframework.web.context.request.async.WebAsyncTask;
import org.springframework.web.context.request.async.WebAsyncUtils;
import org.springframework.web.method.ControllerAdviceBean;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.method.HandlerMethodSelector;
import org.springframework.web.method.annotation.ErrorsMethodArgumentResolver;
import org.springframework.web.method.annotation.ExpressionValueMethodArgumentResolver;
import org.springframework.web.method.annotation.InitBinderDataBinderFactory;
@@ -538,23 +538,31 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter
List<Object> requestResponseBodyAdviceBeans = new ArrayList<Object>();
for (ControllerAdviceBean bean : beans) {
Set<Method> attrMethods = HandlerMethodSelector.selectMethods(bean.getBeanType(), MODEL_ATTRIBUTE_METHODS);
Set<Method> attrMethods = MethodIntrospector.selectMethods(bean.getBeanType(), MODEL_ATTRIBUTE_METHODS);
if (!attrMethods.isEmpty()) {
this.modelAttributeAdviceCache.put(bean, attrMethods);
logger.info("Detected @ModelAttribute methods in " + bean);
if (logger.isInfoEnabled()) {
logger.info("Detected @ModelAttribute methods in " + bean);
}
}
Set<Method> binderMethods = HandlerMethodSelector.selectMethods(bean.getBeanType(), INIT_BINDER_METHODS);
Set<Method> binderMethods = MethodIntrospector.selectMethods(bean.getBeanType(), INIT_BINDER_METHODS);
if (!binderMethods.isEmpty()) {
this.initBinderAdviceCache.put(bean, binderMethods);
logger.info("Detected @InitBinder methods in " + bean);
if (logger.isInfoEnabled()) {
logger.info("Detected @InitBinder methods in " + bean);
}
}
if (RequestBodyAdvice.class.isAssignableFrom(bean.getBeanType())) {
requestResponseBodyAdviceBeans.add(bean);
logger.info("Detected RequestBodyAdvice bean in " + bean);
if (logger.isInfoEnabled()) {
logger.info("Detected RequestBodyAdvice bean in " + bean);
}
}
if (ResponseBodyAdvice.class.isAssignableFrom(bean.getBeanType())) {
requestResponseBodyAdviceBeans.add(bean);
logger.info("Detected ResponseBodyAdvice bean in " + bean);
if (logger.isInfoEnabled()) {
logger.info("Detected ResponseBodyAdvice bean in " + bean);
}
}
}
@@ -826,7 +834,7 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter
Class<?> handlerType = handlerMethod.getBeanType();
Set<Method> methods = this.modelAttributeCache.get(handlerType);
if (methods == null) {
methods = HandlerMethodSelector.selectMethods(handlerType, MODEL_ATTRIBUTE_METHODS);
methods = MethodIntrospector.selectMethods(handlerType, MODEL_ATTRIBUTE_METHODS);
this.modelAttributeCache.put(handlerType, methods);
}
List<InvocableHandlerMethod> attrMethods = new ArrayList<InvocableHandlerMethod>();
@@ -858,7 +866,7 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter
Class<?> handlerType = handlerMethod.getBeanType();
Set<Method> methods = this.initBinderCache.get(handlerType);
if (methods == null) {
methods = HandlerMethodSelector.selectMethods(handlerType, INIT_BINDER_METHODS);
methods = MethodIntrospector.selectMethods(handlerType, INIT_BINDER_METHODS);
this.initBinderCache.put(handlerType, methods);
}
List<InvocableHandlerMethod> initBinderMethods = new ArrayList<InvocableHandlerMethod>();
@@ -925,7 +933,6 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter
* MethodFilter that matches {@link InitBinder @InitBinder} methods.
*/
public static final MethodFilter INIT_BINDER_METHODS = new MethodFilter() {
@Override
public boolean matches(Method method) {
return AnnotationUtils.findAnnotation(method, InitBinder.class) != null;
@@ -936,7 +943,6 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter
* MethodFilter that matches {@link ModelAttribute @ModelAttribute} methods.
*/
public static final MethodFilter MODEL_ATTRIBUTE_METHODS = new MethodFilter() {
@Override
public boolean matches(Method method) {
return ((AnnotationUtils.findAnnotation(method, RequestMapping.class) == null) &&