From 0af6e1bb1b25ac32beb0a629a0e825c82850da21 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Mon, 24 Mar 2014 11:02:03 +0100 Subject: [PATCH] MVC handler method detection ignores scoped targets in favor of proxies Issue: SPR-11548 --- .../handler/AbstractHandlerMethodMapping.java | 19 ++++++- .../WebMvcConfigurationSupportTests.java | 54 +++++++++++++------ 2 files changed, 55 insertions(+), 18 deletions(-) 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 8fa42c40c3..3ed1dd67e2 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 @@ -51,10 +51,24 @@ import org.springframework.web.servlet.HandlerMapping; * * @author Arjen Poutsma * @author Rossen Stoyanchev + * @author Juergen Hoeller * @since 3.1 */ public abstract class AbstractHandlerMethodMapping extends AbstractHandlerMapping implements InitializingBean { + /** + * Bean name prefix for target beans behind scoped proxies. Used to exclude those + * targets from handler method detection, in favor of the corresponding proxies. + *

We're not checking the autowire-candidate status here, which is how the + * proxy target filtering problem is being handled at the autowiring level, + * since autowire-candidate may have been turned to {@code false} for other + * reasons, while still expecting the bean to be eligible for handler methods. + *

Originally defined in {@link org.springframework.aop.scope.ScopedProxyUtils} + * but duplicated here to avoid a hard dependency on the spring-aop module. + */ + private static final String SCOPED_TARGET_NAME_PREFIX = "scopedTarget."; + + private boolean detectHandlerMethodsInAncestorContexts = false; private final Map handlerMethods = new LinkedHashMap(); @@ -105,7 +119,8 @@ public abstract class AbstractHandlerMethodMapping extends AbstractHandlerMap getApplicationContext().getBeanNamesForType(Object.class)); for (String beanName : beanNames) { - if (isHandler(getApplicationContext().getType(beanName))){ + if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX) && + isHandler(getApplicationContext().getType(beanName))){ detectHandlerMethods(beanName); } } @@ -127,7 +142,7 @@ public abstract class AbstractHandlerMethodMapping extends AbstractHandlerMap Class handlerType = (handler instanceof String ? getApplicationContext().getType((String) handler) : handler.getClass()); - // Avoid repeated calls to getMappingForMethod which would rebuild RequestMatchingInfo instances + // Avoid repeated calls to getMappingForMethod which would rebuild RequestMappingInfo instances final Map mappings = new IdentityHashMap(); final Class userType = ClassUtils.getUserClass(handlerType); diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurationSupportTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurationSupportTests.java index 57c6866f40..942d58e160 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurationSupportTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurationSupportTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2014 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,16 @@ package org.springframework.web.servlet.config.annotation; import java.util.List; - import javax.servlet.http.HttpServletRequest; import org.joda.time.DateTime; import org.junit.Before; import org.junit.Test; + import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Scope; +import org.springframework.context.annotation.ScopedProxyMode; import org.springframework.core.convert.ConversionService; import org.springframework.format.annotation.DateTimeFormat; import org.springframework.format.annotation.DateTimeFormat.ISO; @@ -49,10 +51,10 @@ import org.springframework.web.servlet.handler.ConversionServiceExposingIntercep import org.springframework.web.servlet.handler.HandlerExceptionResolverComposite; import org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver; import org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver; +import org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder; import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter; import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; import org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver; -import org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder; import static org.junit.Assert.*; @@ -60,6 +62,7 @@ import static org.junit.Assert.*; * A test fixture with an {@link WebMvcConfigurationSupport} instance. * * @author Rossen Stoyanchev + * @author Juergen Hoeller */ public class WebMvcConfigurationSupportTests { @@ -68,29 +71,34 @@ public class WebMvcConfigurationSupportTests { @Before public void setUp() { - AnnotationConfigWebApplicationContext cxt = new AnnotationConfigWebApplicationContext(); cxt.setServletContext(new MockServletContext()); - cxt.register(TestConfig.class); + cxt.register(TestConfig.class, ScopedController.class, ScopedProxyController.class); cxt.refresh(); this.wac = cxt; } + @Test public void requestMappingHandlerMapping() throws Exception { - RequestMappingHandlerMapping handlerMapping = this.wac.getBean(RequestMappingHandlerMapping.class); assertEquals(0, handlerMapping.getOrder()); HandlerExecutionChain chain = handlerMapping.getHandler(new MockHttpServletRequest("GET", "/")); + assertNotNull(chain); assertNotNull(chain.getInterceptors()); assertEquals(ConversionServiceExposingInterceptor.class, chain.getInterceptors()[0].getClass()); + + chain = handlerMapping.getHandler(new MockHttpServletRequest("GET", "/scoped")); + assertNotNull(chain); + + chain = handlerMapping.getHandler(new MockHttpServletRequest("GET", "/scopedProxy")); + assertNotNull(chain); } @Test public void emptyViewControllerHandlerMapping() { - AbstractHandlerMapping handlerMapping = this.wac.getBean( "viewControllerHandlerMapping", AbstractHandlerMapping.class); @@ -101,7 +109,6 @@ public class WebMvcConfigurationSupportTests { @Test public void beanNameHandlerMapping() throws Exception { - BeanNameUrlHandlerMapping handlerMapping = this.wac.getBean(BeanNameUrlHandlerMapping.class); assertEquals(2, handlerMapping.getOrder()); @@ -115,7 +122,6 @@ public class WebMvcConfigurationSupportTests { @Test public void emptyResourceHandlerMapping() { - AbstractHandlerMapping handlerMapping = this.wac.getBean( "resourceHandlerMapping", AbstractHandlerMapping.class); @@ -126,7 +132,6 @@ public class WebMvcConfigurationSupportTests { @Test public void emptyDefaultServletHandlerMapping() { - AbstractHandlerMapping handlerMapping = this.wac.getBean( "defaultServletHandlerMapping", AbstractHandlerMapping.class); @@ -137,9 +142,7 @@ public class WebMvcConfigurationSupportTests { @Test public void requestMappingHandlerAdapter() throws Exception { - RequestMappingHandlerAdapter adapter = this.wac.getBean(RequestMappingHandlerAdapter.class); - assertEquals(9, adapter.getMessageConverters().size()); ConfigurableWebBindingInitializer initializer = (ConfigurableWebBindingInitializer) adapter.getWebBindingInitializer(); @@ -156,7 +159,6 @@ public class WebMvcConfigurationSupportTests { @Test public void uriComponentsContributor() throws Exception { - CompositeUriComponentsContributor uriComponentsContributor = this.wac.getBean( MvcUriComponentsBuilder.MVC_URI_COMPONENTS_CONTRIBUTOR_BEAN_NAME, CompositeUriComponentsContributor.class); @@ -166,7 +168,6 @@ public class WebMvcConfigurationSupportTests { @Test public void handlerExceptionResolver() throws Exception { - HandlerExceptionResolverComposite compositeResolver = this.wac.getBean("handlerExceptionResolver", HandlerExceptionResolverComposite.class); @@ -187,14 +188,15 @@ public class WebMvcConfigurationSupportTests { @Configuration public static class TestConfig { - @Bean(name={"/testController"}) + @Bean(name="/testController") public TestController testController() { return new TestController(); } } + @Controller - private static class TestController { + public static class TestController { @RequestMapping("/") public void handle() { @@ -207,4 +209,24 @@ public class WebMvcConfigurationSupportTests { } } + + @Controller + @Scope("prototype") + public static class ScopedController { + + @RequestMapping("/scoped") + public void handle() { + } + } + + + @Controller + @Scope(value="prototype", proxyMode=ScopedProxyMode.TARGET_CLASS) + public static class ScopedProxyController { + + @RequestMapping("/scopedProxy") + public void handle() { + } + } + }