diff --git a/spring-core/src/main/java/org/springframework/core/MethodIntrospector.java b/spring-core/src/main/java/org/springframework/core/MethodIntrospector.java new file mode 100644 index 0000000000..d402c10b6d --- /dev/null +++ b/spring-core/src/main/java/org/springframework/core/MethodIntrospector.java @@ -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. + * + *

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

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 Map selectMethods(Class targetType, final MetadataLookup metadataLookup) { + final Map methodMap = new LinkedHashMap(); + Set> handlerTypes = new LinkedHashSet>(); + 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. + *

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 selectMethods(Class targetType, final ReflectionUtils.MethodFilter methodFilter) { + return selectMethods(targetType, new MetadataLookup() { + @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. + *

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 the type of metadata returned + */ + public interface MetadataLookup { + + /** + * 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); + } + +} diff --git a/spring-messaging/src/main/java/org/springframework/messaging/handler/HandlerMethodSelector.java b/spring-messaging/src/main/java/org/springframework/messaging/handler/HandlerMethodSelector.java index 86b0a1fa41..69bd0c1aca 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/handler/HandlerMethodSelector.java +++ b/spring-messaging/src/main/java/org/springframework/messaging/handler/HandlerMethodSelector.java @@ -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 selectMethods(final Class handlerType, final MethodFilter handlerMethodFilter) { - final Set handlerMethods = new LinkedHashSet(); - Set> handlerTypes = new LinkedHashSet>(); - 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 selectMethods(Class handlerType, MethodFilter handlerMethodFilter) { + return MethodIntrospector.selectMethods(handlerType, handlerMethodFilter); } } diff --git a/spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/support/AnnotationExceptionHandlerMethodResolver.java b/spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/support/AnnotationExceptionHandlerMethodResolver.java index 03f98a6ab7..cccbc446bc 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/support/AnnotationExceptionHandlerMethodResolver.java +++ b/spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/support/AnnotationExceptionHandlerMethodResolver.java @@ -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, Method> initExceptionMappings(Class handlerType) { + Map methods = MethodIntrospector.selectMethods(handlerType, + new MethodIntrospector.MetadataLookup() { + @Override + public MessageExceptionHandler inspect(Method method) { + return AnnotationUtils.findAnnotation(method, MessageExceptionHandler.class); + } + }); + Map, Method> result = new HashMap, Method>(); - for (Method method : HandlerMethodSelector.selectMethods(handlerType, EXCEPTION_HANDLER_METHOD_FILTER)) { - for (Class exceptionType : getMappedExceptions(method)) { + for (Map.Entry entry : methods.entrySet()) { + Method method = entry.getKey(); + List> exceptionTypes = new ArrayList>(); + exceptionTypes.addAll(Arrays.asList(entry.getValue().value())); + if (exceptionTypes.isEmpty()) { + exceptionTypes.addAll(getExceptionsFromMethodSignature(method)); + } + for (Class 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> getMappedExceptions(Method method) { - List> result = new ArrayList>(); - 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 diff --git a/spring-messaging/src/main/java/org/springframework/messaging/handler/invocation/AbstractMethodMessageHandler.java b/spring-messaging/src/main/java/org/springframework/messaging/handler/invocation/AbstractMethodMessageHandler.java index 69c01809af..639e241560 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/handler/invocation/AbstractMethodMessageHandler.java +++ b/spring-messaging/src/main/java/org/springframework/messaging/handler/invocation/AbstractMethodMessageHandler.java @@ -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 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 * 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 methods = HandlerMethodSelector.selectMethods(userType, new ReflectionUtils.MethodFilter() { - @Override - public boolean matches(Method method) { - return getMappingForMethod(method, userType) != null; - } - }); + Map methods = MethodIntrospector.selectMethods(userType, + new MethodIntrospector.MetadataLookup() { + @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 entry : methods.entrySet()) { + registerHandlerMethod(handler, entry.getKey(), entry.getValue()); } } @@ -277,7 +280,6 @@ public abstract class AbstractMethodMessageHandler */ 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 * 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); diff --git a/spring-web/src/main/java/org/springframework/web/method/HandlerMethodSelector.java b/spring-web/src/main/java/org/springframework/web/method/HandlerMethodSelector.java index a659ff2860..82acd2c3bd 100644 --- a/spring-web/src/main/java/org/springframework/web/method/HandlerMethodSelector.java +++ b/spring-web/src/main/java/org/springframework/web/method/HandlerMethodSelector.java @@ -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 selectMethods(final Class handlerType, final MethodFilter handlerMethodFilter) { - final Set handlerMethods = new LinkedHashSet(); - Set> handlerTypes = new LinkedHashSet>(); - 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 selectMethods(Class handlerType, MethodFilter handlerMethodFilter) { + return MethodIntrospector.selectMethods(handlerType, handlerMethodFilter); } } diff --git a/spring-web/src/main/java/org/springframework/web/method/annotation/ExceptionHandlerMethodResolver.java b/spring-web/src/main/java/org/springframework/web/method/annotation/ExceptionHandlerMethodResolver.java index 46af047ad8..5d307cba90 100644 --- a/spring-web/src/main/java/org/springframework/web/method/annotation/ExceptionHandlerMethodResolver.java +++ b/spring-web/src/main/java/org/springframework/web/method/annotation/ExceptionHandlerMethodResolver.java @@ -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 exceptionType : detectExceptionMappings(method)) { addExceptionMapping(exceptionType, method); } @@ -105,9 +105,8 @@ public class ExceptionHandlerMethodResolver { private void addExceptionMapping(Class 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 + "}"); } } diff --git a/spring-web/src/test/java/org/springframework/web/method/annotation/ModelFactoryOrderingTests.java b/spring-web/src/test/java/org/springframework/web/method/annotation/ModelFactoryOrderingTests.java index 793cc67d50..8d5c7aed34 100644 --- a/spring-web/src/test/java/org/springframework/web/method/annotation/ModelFactoryOrderingTests.java +++ b/spring-web/src/test/java/org/springframework/web/method/annotation/ModelFactoryOrderingTests.java @@ -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 methods = HandlerMethodSelector.selectMethods(type, METHOD_FILTER); + Set methods = MethodIntrospector.selectMethods(type, METHOD_FILTER); List modelMethods = new ArrayList(); for (Method method : methods) { InvocableHandlerMethod modelMethod = new InvocableHandlerMethod(controller, method); diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/handler/AbstractHandlerMethodMapping.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/handler/AbstractHandlerMethodMapping.java index bf4eb7d131..7e1c50f12d 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/handler/AbstractHandlerMethodMapping.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/handler/AbstractHandlerMethodMapping.java @@ -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 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 mappings = new IdentityHashMap(); + Class handlerType = (handler instanceof String ? + getApplicationContext().getType((String) handler) : handler.getClass()); final Class userType = ClassUtils.getUserClass(handlerType); - Set 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 methods = MethodIntrospector.selectMethods(userType, + new MethodIntrospector.MetadataLookup() { + @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 entry : methods.entrySet()) { + registerHandlerMethod(handler, entry.getKey(), entry.getValue()); } } @@ -241,12 +233,7 @@ public abstract class AbstractHandlerMethodMapping 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 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 extends AbstractHandlerMap private final Map corsLookup = new ConcurrentHashMap(); - 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 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 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 extends AbstractHandlerMap } private void addMappingName(String name, HandlerMethod handlerMethod) { - - List oldList = this.nameLookup.containsKey(name) ? - this.nameLookup.get(name) : Collections.emptyList(); + List oldList = this.nameLookup.get(name); + if (oldList == null) { + oldList = Collections.emptyList(); + } for (HandlerMethod current : oldList) { if (handlerMethod.equals(current)) { @@ -587,7 +572,7 @@ public abstract class AbstractHandlerMethodMapping extends AbstractHandlerMap } if (logger.isTraceEnabled()) { - logger.trace("Mapping name=" + name); + logger.trace("Mapping name '" + name + "'"); } List newList = new ArrayList(oldList.size() + 1); @@ -597,7 +582,7 @@ public abstract class AbstractHandlerMethodMapping 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 extends AbstractHandlerMap } this.nameLookup.put(name, newList); } - } + private static class MappingRegistration { private final T mapping; @@ -667,20 +652,15 @@ public abstract class AbstractHandlerMethodMapping extends AbstractHandlerMap private final String mappingName; - - public MappingRegistration(T mapping, HandlerMethod handlerMethod, - List directUrls, String mappingName) { - + public MappingRegistration(T mapping, HandlerMethod handlerMethod, List directUrls, String mappingName) { Assert.notNull(mapping); Assert.notNull(handlerMethod); - this.mapping = mapping; this.handlerMethod = handlerMethod; this.directUrls = (directUrls != null ? directUrls : Collections.emptyList()); this.mappingName = mappingName; } - public T getMapping() { return this.mapping; } diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/MvcUriComponentsBuilder.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/MvcUriComponentsBuilder.java index bc2cb41831..e8ff7ee93a 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/MvcUriComponentsBuilder.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/MvcUriComponentsBuilder.java @@ -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 methods = HandlerMethodSelector.selectMethods(controllerType, selector); + Set methods = MethodIntrospector.selectMethods(controllerType, selector); if (methods.size() == 1) { return methods.iterator().next(); } diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerAdapter.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerAdapter.java index b0e087414b..a1c8e09443 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerAdapter.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerAdapter.java @@ -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 requestResponseBodyAdviceBeans = new ArrayList(); for (ControllerAdviceBean bean : beans) { - Set attrMethods = HandlerMethodSelector.selectMethods(bean.getBeanType(), MODEL_ATTRIBUTE_METHODS); + Set 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 binderMethods = HandlerMethodSelector.selectMethods(bean.getBeanType(), INIT_BINDER_METHODS); + Set 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 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 attrMethods = new ArrayList(); @@ -858,7 +866,7 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter Class handlerType = handlerMethod.getBeanType(); Set 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 initBinderMethods = new ArrayList(); @@ -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) &&