diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/annotation/DefaultAnnotationHandlerMapping.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/annotation/DefaultAnnotationHandlerMapping.java index f2087c192a0..e7a858cb8e2 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/annotation/DefaultAnnotationHandlerMapping.java +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/annotation/DefaultAnnotationHandlerMapping.java @@ -17,6 +17,7 @@ package org.springframework.web.servlet.mvc.annotation; import java.lang.reflect.Method; +import java.lang.reflect.Proxy; import java.util.HashMap; import java.util.LinkedHashSet; import java.util.Map; @@ -147,17 +148,21 @@ public class DefaultAnnotationHandlerMapping extends AbstractDetectingUrlHandler */ protected String[] determineUrlsForHandlerMethods(Class handlerType) { final Set urls = new LinkedHashSet(); - ReflectionUtils.doWithMethods(handlerType, new ReflectionUtils.MethodCallback() { - public void doWith(Method method) { - RequestMapping mapping = method.getAnnotation(RequestMapping.class); - if (mapping != null) { - String[] mappedPaths = mapping.value(); - for (String mappedPath : mappedPaths) { - addUrlsForPath(urls, mappedPath); + Class[] handlerTypes = + Proxy.isProxyClass(handlerType) ? handlerType.getInterfaces() : new Class[]{handlerType}; + for (Class currentHandlerType : handlerTypes) { + ReflectionUtils.doWithMethods(currentHandlerType, new ReflectionUtils.MethodCallback() { + public void doWith(Method method) { + RequestMapping mapping = AnnotationUtils.findAnnotation(method, RequestMapping.class); + if (mapping != null) { + String[] mappedPaths = mapping.value(); + for (String mappedPath : mappedPaths) { + addUrlsForPath(urls, mappedPath); + } } } - } - }); + }); + } return StringUtils.toStringArray(urls); } diff --git a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/annotation/JdkProxyServletAnnotationTests.java b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/annotation/JdkProxyServletAnnotationTests.java new file mode 100644 index 00000000000..527b1ac6ba6 --- /dev/null +++ b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/annotation/JdkProxyServletAnnotationTests.java @@ -0,0 +1,149 @@ +/* + * Copyright 2002-2009 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.web.servlet.mvc.annotation; + +import java.io.IOException; +import java.io.Writer; +import javax.servlet.ServletException; + +import static org.junit.Assert.*; +import org.junit.Test; + +import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator; +import org.springframework.aop.interceptor.SimpleTraceInterceptor; +import org.springframework.aop.support.DefaultPointcutAdvisor; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.support.RootBeanDefinition; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; +import org.springframework.mock.web.MockServletConfig; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.context.WebApplicationContext; +import org.springframework.web.context.support.GenericWebApplicationContext; +import org.springframework.web.servlet.DispatcherServlet; + +/** + * @author Arjen Poutsma + * @since 3.0 + */ +public class JdkProxyServletAnnotationTests { + + private DispatcherServlet servlet; + + @Test + public void typeLevel() throws Exception { + initServlet(TypeLevelImpl.class); + + MockHttpServletRequest request = new MockHttpServletRequest("GET", "/test"); + MockHttpServletResponse response = new MockHttpServletResponse(); + servlet.service(request, response); + assertEquals("doIt", response.getContentAsString()); + } + + @Test + public void methodLevel() throws Exception { + initServlet(MethodLevelImpl.class); + + MockHttpServletRequest request = new MockHttpServletRequest("GET", "/test"); + MockHttpServletResponse response = new MockHttpServletResponse(); + servlet.service(request, response); + assertEquals("doIt", response.getContentAsString()); + } + + @Test + public void typeAndMethodLevel() throws Exception { + initServlet(TypeAndMethodLevelImpl.class); + + MockHttpServletRequest request = new MockHttpServletRequest("GET", "/hotels/bookings"); + MockHttpServletResponse response = new MockHttpServletResponse(); + servlet.service(request, response); + assertEquals("doIt", response.getContentAsString()); + } + + + private void initServlet(final Class controllerclass) throws ServletException { + servlet = new DispatcherServlet() { + @Override + protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent) + throws BeansException { + GenericWebApplicationContext wac = new GenericWebApplicationContext(); + wac.registerBeanDefinition("controller", new RootBeanDefinition(controllerclass)); + DefaultAdvisorAutoProxyCreator autoProxyCreator = new DefaultAdvisorAutoProxyCreator(); + autoProxyCreator.setBeanFactory(wac.getBeanFactory()); + wac.getBeanFactory().addBeanPostProcessor(autoProxyCreator); + wac.getBeanFactory().registerSingleton("advisor", new DefaultPointcutAdvisor(new SimpleTraceInterceptor(true))); + wac.refresh(); + return wac; + } + }; + servlet.init(new MockServletConfig()); + } + + /* + * Controllers + */ + + @Controller + @RequestMapping("/test") + public interface TypeLevel { + + @RequestMapping + void doIt(Writer writer) throws IOException; + + } + + public static class TypeLevelImpl implements TypeLevel { + + public void doIt(Writer writer) throws IOException { + writer.write("doIt"); + } + } + + + @Controller + public interface MethodLevel { + + @RequestMapping("/test") + void doIt(Writer writer) throws IOException; + + } + + public static class MethodLevelImpl implements MethodLevel { + + public void doIt(Writer writer) throws IOException { + writer.write("doIt"); + } + } + + @Controller + @RequestMapping("/hotels") + public interface TypeAndMethodLevel { + + @RequestMapping("/bookings") + void doIt(Writer writer) throws IOException; + + } + + public static class TypeAndMethodLevelImpl implements TypeAndMethodLevel { + + public void doIt(Writer writer) throws IOException { + writer.write("doIt"); + } + } + +} diff --git a/org.springframework.web/src/main/java/org/springframework/web/bind/annotation/support/HandlerMethodResolver.java b/org.springframework.web/src/main/java/org/springframework/web/bind/annotation/support/HandlerMethodResolver.java index ee350dfa27d..5071031d4da 100644 --- a/org.springframework.web/src/main/java/org/springframework/web/bind/annotation/support/HandlerMethodResolver.java +++ b/org.springframework.web/src/main/java/org/springframework/web/bind/annotation/support/HandlerMethodResolver.java @@ -17,12 +17,14 @@ package org.springframework.web.bind.annotation.support; import java.lang.reflect.Method; +import java.lang.reflect.Proxy; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.Set; +import org.springframework.core.annotation.AnnotationUtils; import org.springframework.util.ClassUtils; import org.springframework.util.ReflectionUtils; import org.springframework.web.bind.annotation.InitBinder; @@ -68,21 +70,25 @@ public class HandlerMethodResolver { * Initialize a new HandlerMethodResolver for the specified handler type. * @param handlerType the handler class to introspect */ - public void init(final Class handlerType) { - ReflectionUtils.doWithMethods(handlerType, new ReflectionUtils.MethodCallback() { - public void doWith(Method method) { - if (isHandlerMethod(method)) { - handlerMethods.add(ClassUtils.getMostSpecificMethod(method, handlerType)); + public void init(Class handlerType) { + Class[] handlerTypes = + Proxy.isProxyClass(handlerType) ? handlerType.getInterfaces() : new Class[]{handlerType}; + for (final Class currentHandlerType : handlerTypes) { + ReflectionUtils.doWithMethods(currentHandlerType, new ReflectionUtils.MethodCallback() { + public void doWith(Method method) { + if (isHandlerMethod(ClassUtils.getMostSpecificMethod(method, currentHandlerType))) { + handlerMethods.add(ClassUtils.getMostSpecificMethod(method, currentHandlerType)); + } + else if (method.isAnnotationPresent(InitBinder.class)) { + initBinderMethods.add(ClassUtils.getMostSpecificMethod(method, currentHandlerType)); + } + else if (method.isAnnotationPresent(ModelAttribute.class)) { + modelAttributeMethods.add(ClassUtils.getMostSpecificMethod(method, currentHandlerType)); + } } - else if (method.isAnnotationPresent(InitBinder.class)) { - initBinderMethods.add(ClassUtils.getMostSpecificMethod(method, handlerType)); - } - else if (method.isAnnotationPresent(ModelAttribute.class)) { - modelAttributeMethods.add(ClassUtils.getMostSpecificMethod(method, handlerType)); - } - } - }); - this.typeLevelMapping = handlerType.getAnnotation(RequestMapping.class); + }); + } + this.typeLevelMapping = AnnotationUtils.findAnnotation(handlerType, RequestMapping.class); SessionAttributes sessionAttributes = handlerType.getAnnotation(SessionAttributes.class); this.sessionAttributesFound = (sessionAttributes != null); if (this.sessionAttributesFound) { @@ -92,7 +98,7 @@ public class HandlerMethodResolver { } protected boolean isHandlerMethod(Method method) { - return method.isAnnotationPresent(RequestMapping.class); + return AnnotationUtils.findAnnotation(method, RequestMapping.class) != null; }