Add a workaround for DATACMNS-776

Spring Data’s web support includes a handler method argument resolver,
ProxyingHandlerMethodArgumentResolver, that inaccurately claims that it
can handle all interface handler method arguments. This causes problems
for handler methods that take Spring Mobile’s Device as an argument as
the proxied Device instance does not behave correctly.

This commit works around the problem by assigning an order to the 
WebMvcConfigurerAdapter that registers Spring Mobile’s argument resolver
with Spring MVC. This ordering ensures that Spring Mobile’s resolver
takes precedence over Spring Data’s for Device arguments.

Closes gh-4163
pull/4783/merge
Andy Wilkinson 9 years ago
parent c7c685f65f
commit 037a27e257

@ -27,6 +27,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplicat
import org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.mobile.device.DeviceHandlerMethodArgumentResolver;
import org.springframework.mobile.device.DeviceResolver;
import org.springframework.mobile.device.DeviceResolverHandlerInterceptor;
@ -48,6 +49,7 @@ public class DeviceResolverAutoConfiguration {
@Configuration
@ConditionalOnWebApplication
@Order(0)
protected static class DeviceResolverMvcConfiguration
extends WebMvcConfigurerAdapter {

@ -20,15 +20,23 @@ import org.junit.After;
import org.junit.Test;
import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.autoconfigure.data.rest.RepositoryRestMvcAutoConfiguration;
import org.springframework.boot.autoconfigure.data.web.SpringDataWebAutoConfiguration;
import org.springframework.boot.autoconfigure.test.ImportAutoConfiguration;
import org.springframework.boot.autoconfigure.web.HttpMessageConvertersAutoConfiguration;
import org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.mobile.device.Device;
import org.springframework.mobile.device.DeviceHandlerMethodArgumentResolver;
import org.springframework.mobile.device.DeviceResolverHandlerInterceptor;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockServletContext;
import org.springframework.stereotype.Controller;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.HandlerInterceptor;
@ -38,6 +46,8 @@ import static org.hamcrest.Matchers.hasItemInArray;
import static org.hamcrest.Matchers.instanceOf;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThat;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
/**
* Tests for {@link DeviceResolverAutoConfiguration}.
@ -76,10 +86,7 @@ public class DeviceResolverAutoConfigurationTests {
public void deviceResolverHandlerInterceptorRegistered() throws Exception {
this.context = new AnnotationConfigWebApplicationContext();
this.context.setServletContext(new MockServletContext());
this.context.register(Config.class, WebMvcAutoConfiguration.class,
HttpMessageConvertersAutoConfiguration.class,
DeviceResolverAutoConfiguration.class,
PropertyPlaceholderAutoConfiguration.class);
this.context.register(Config.class);
this.context.refresh();
RequestMappingHandlerMapping mapping = this.context
.getBean(RequestMappingHandlerMapping.class);
@ -89,7 +96,23 @@ public class DeviceResolverAutoConfigurationTests {
hasItemInArray(instanceOf(DeviceResolverHandlerInterceptor.class)));
}
@Test
public void deviceHandlerMethodArgumentWorksWithSpringData() throws Exception {
this.context = new AnnotationConfigWebApplicationContext();
this.context.register(Config.class);
this.context.setServletContext(new MockServletContext());
this.context.refresh();
MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(this.context).build();
mockMvc.perform(get("/")).andExpect(status().isOk());
}
@Configuration
@ImportAutoConfiguration({ WebMvcAutoConfiguration.class,
HttpMessageConvertersAutoConfiguration.class,
DeviceResolverAutoConfiguration.class,
PropertyPlaceholderAutoConfiguration.class,
SpringDataWebAutoConfiguration.class,
RepositoryRestMvcAutoConfiguration.class })
protected static class Config {
@Bean
@ -103,8 +126,11 @@ public class DeviceResolverAutoConfigurationTests {
protected static class MyController {
@RequestMapping("/")
public void test() {
public ResponseEntity<Void> test(Device device) {
if (device.getDevicePlatform() != null) {
return new ResponseEntity<>(HttpStatus.OK);
}
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
}
}

Loading…
Cancel
Save