diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/ModelAttributeMethodArgumentResolver.java b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/ModelAttributeMethodArgumentResolver.java index 4da0f0d53f..4294235fa2 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/ModelAttributeMethodArgumentResolver.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/ModelAttributeMethodArgumentResolver.java @@ -60,6 +60,7 @@ import org.springframework.web.server.ServerWebExchange; * * @author Rossen Stoyanchev * @author Juergen Hoeller + * @author Sam Brannen * @since 5.0 */ public class ModelAttributeMethodArgumentResolver extends HandlerMethodArgumentResolverSupport { @@ -117,7 +118,7 @@ public class ModelAttributeMethodArgumentResolver extends HandlerMethodArgumentR return valueMono.flatMap(value -> { WebExchangeDataBinder binder = context.createDataBinder(exchange, value, name); - return bindRequestParameters(binder, exchange) + return (bindingDisabled(parameter) ? Mono.empty() : bindRequestParameters(binder, exchange)) .doOnError(bindingResultSink::tryEmitError) .doOnSuccess(aVoid -> { validateIfApplicable(binder, parameter); @@ -143,6 +144,16 @@ public class ModelAttributeMethodArgumentResolver extends HandlerMethodArgumentR }); } + /** + * Determine if binding should be disabled for the supplied {@link MethodParameter}, + * based on the {@link ModelAttribute#binding} annotation attribute. + * @since 5.3.7 + */ + private boolean bindingDisabled(MethodParameter parameter) { + ModelAttribute modelAttribute = parameter.getParameterAnnotation(ModelAttribute.class); + return (modelAttribute != null && !modelAttribute.binding()); + } + /** * Extension point to bind the request to the target object. * @param binder the data binder instance to use for the binding diff --git a/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/ModelAttributeMethodArgumentResolverTests.java b/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/ModelAttributeMethodArgumentResolverTests.java index cb8052d751..514dd48d95 100644 --- a/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/ModelAttributeMethodArgumentResolverTests.java +++ b/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/ModelAttributeMethodArgumentResolverTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2021 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. @@ -21,6 +21,8 @@ import java.time.Duration; import java.util.Map; import java.util.function.Function; +import javax.validation.constraints.NotEmpty; + import io.reactivex.rxjava3.core.Single; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -49,16 +51,17 @@ import static org.assertj.core.api.Assertions.assertThat; * * @author Rossen Stoyanchev * @author Juergen Hoeller + * @author Sam Brannen */ -public class ModelAttributeMethodArgumentResolverTests { +class ModelAttributeMethodArgumentResolverTests { - private BindingContext bindContext; + private final ResolvableMethod testMethod = ResolvableMethod.on(getClass()).named("handle").build(); - private ResolvableMethod testMethod = ResolvableMethod.on(getClass()).named("handle").build(); + private BindingContext bindContext; @BeforeEach - public void setup() throws Exception { + void setup() { LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean(); validator.afterPropertiesSet(); ConfigurableWebBindingInitializer initializer = new ConfigurableWebBindingInitializer(); @@ -68,32 +71,38 @@ public class ModelAttributeMethodArgumentResolverTests { @Test - public void supports() throws Exception { + void supports() { ModelAttributeMethodArgumentResolver resolver = new ModelAttributeMethodArgumentResolver(ReactiveAdapterRegistry.getSharedInstance(), false); - MethodParameter param = this.testMethod.annotPresent(ModelAttribute.class).arg(Foo.class); + MethodParameter param = this.testMethod.annotPresent(ModelAttribute.class).arg(Pojo.class); assertThat(resolver.supportsParameter(param)).isTrue(); - param = this.testMethod.annotPresent(ModelAttribute.class).arg(Mono.class, Foo.class); + param = this.testMethod.annotPresent(ModelAttribute.class).arg(NonBindingPojo.class); assertThat(resolver.supportsParameter(param)).isTrue(); - param = this.testMethod.annotNotPresent(ModelAttribute.class).arg(Foo.class); + param = this.testMethod.annotPresent(ModelAttribute.class).arg(Mono.class, Pojo.class); + assertThat(resolver.supportsParameter(param)).isTrue(); + + param = this.testMethod.annotPresent(ModelAttribute.class).arg(Mono.class, NonBindingPojo.class); + assertThat(resolver.supportsParameter(param)).isTrue(); + + param = this.testMethod.annotNotPresent(ModelAttribute.class).arg(Pojo.class); assertThat(resolver.supportsParameter(param)).isFalse(); - param = this.testMethod.annotNotPresent(ModelAttribute.class).arg(Mono.class, Foo.class); + param = this.testMethod.annotNotPresent(ModelAttribute.class).arg(Mono.class, Pojo.class); assertThat(resolver.supportsParameter(param)).isFalse(); } @Test - public void supportsWithDefaultResolution() throws Exception { + void supportsWithDefaultResolution() { ModelAttributeMethodArgumentResolver resolver = new ModelAttributeMethodArgumentResolver(ReactiveAdapterRegistry.getSharedInstance(), true); - MethodParameter param = this.testMethod.annotNotPresent(ModelAttribute.class).arg(Foo.class); + MethodParameter param = this.testMethod.annotNotPresent(ModelAttribute.class).arg(Pojo.class); assertThat(resolver.supportsParameter(param)).isTrue(); - param = this.testMethod.annotNotPresent(ModelAttribute.class).arg(Mono.class, Foo.class); + param = this.testMethod.annotNotPresent(ModelAttribute.class).arg(Mono.class, Pojo.class); assertThat(resolver.supportsParameter(param)).isTrue(); param = this.testMethod.annotNotPresent(ModelAttribute.class).arg(String.class); @@ -104,204 +113,286 @@ public class ModelAttributeMethodArgumentResolverTests { } @Test - public void createAndBind() throws Exception { - testBindFoo("foo", this.testMethod.annotPresent(ModelAttribute.class).arg(Foo.class), value -> { - assertThat(value.getClass()).isEqualTo(Foo.class); - return (Foo) value; + void createAndBind() throws Exception { + testBindPojo("pojo", this.testMethod.annotPresent(ModelAttribute.class).arg(Pojo.class), value -> { + assertThat(value.getClass()).isEqualTo(Pojo.class); + return (Pojo) value; }); } @Test - public void createAndBindToMono() throws Exception { + void createAndBindToMono() throws Exception { MethodParameter parameter = this.testMethod - .annotNotPresent(ModelAttribute.class).arg(Mono.class, Foo.class); + .annotNotPresent(ModelAttribute.class).arg(Mono.class, Pojo.class); - testBindFoo("fooMono", parameter, mono -> { - boolean condition = mono instanceof Mono; - assertThat(condition).as(mono.getClass().getName()).isTrue(); + testBindPojo("pojoMono", parameter, mono -> { + assertThat(mono).isInstanceOf(Mono.class); Object value = ((Mono) mono).block(Duration.ofSeconds(5)); - assertThat(value.getClass()).isEqualTo(Foo.class); - return (Foo) value; + assertThat(value.getClass()).isEqualTo(Pojo.class); + return (Pojo) value; }); } @Test - public void createAndBindToSingle() throws Exception { + void createAndBindToSingle() throws Exception { MethodParameter parameter = this.testMethod - .annotPresent(ModelAttribute.class).arg(Single.class, Foo.class); + .annotPresent(ModelAttribute.class).arg(Single.class, Pojo.class); - testBindFoo("fooSingle", parameter, single -> { - boolean condition = single instanceof Single; - assertThat(condition).as(single.getClass().getName()).isTrue(); + testBindPojo("pojoSingle", parameter, single -> { + assertThat(single).isInstanceOf(Single.class); Object value = ((Single) single).blockingGet(); - assertThat(value.getClass()).isEqualTo(Foo.class); - return (Foo) value; + assertThat(value.getClass()).isEqualTo(Pojo.class); + return (Pojo) value; }); } @Test - public void bindExisting() throws Exception { - Foo foo = new Foo(); - foo.setName("Jim"); - this.bindContext.getModel().addAttribute(foo); - - MethodParameter parameter = this.testMethod.annotNotPresent(ModelAttribute.class).arg(Foo.class); - testBindFoo("foo", parameter, value -> { - assertThat(value.getClass()).isEqualTo(Foo.class); - return (Foo) value; + void createButDoNotBind() throws Exception { + MethodParameter parameter = + this.testMethod.annotPresent(ModelAttribute.class).arg(NonBindingPojo.class); + + createButDoNotBindToPojo("nonBindingPojo", parameter, value -> { + assertThat(value).isInstanceOf(NonBindingPojo.class); + return (NonBindingPojo) value; }); + } - assertThat(this.bindContext.getModel().asMap().get("foo")).isSameAs(foo); + @Test + void createButDoNotBindToMono() throws Exception { + MethodParameter parameter = + this.testMethod.annotPresent(ModelAttribute.class).arg(Mono.class, NonBindingPojo.class); + + createButDoNotBindToPojo("nonBindingPojoMono", parameter, value -> { + assertThat(value).isInstanceOf(Mono.class); + Object extractedValue = ((Mono) value).block(Duration.ofSeconds(5)); + assertThat(extractedValue).isInstanceOf(NonBindingPojo.class); + return (NonBindingPojo) extractedValue; + }); } @Test - public void bindExistingMono() throws Exception { - Foo foo = new Foo(); - foo.setName("Jim"); - this.bindContext.getModel().addAttribute("fooMono", Mono.just(foo)); - - MethodParameter parameter = this.testMethod.annotNotPresent(ModelAttribute.class).arg(Foo.class); - testBindFoo("foo", parameter, value -> { - assertThat(value.getClass()).isEqualTo(Foo.class); - return (Foo) value; + void createButDoNotBindToSingle() throws Exception { + MethodParameter parameter = + this.testMethod.annotPresent(ModelAttribute.class).arg(Single.class, NonBindingPojo.class); + + createButDoNotBindToPojo("nonBindingPojoSingle", parameter, value -> { + assertThat(value).isInstanceOf(Single.class); + Object extractedValue = ((Single) value).blockingGet(); + assertThat(extractedValue).isInstanceOf(NonBindingPojo.class); + return (NonBindingPojo) extractedValue; }); + } + + private void createButDoNotBindToPojo(String modelKey, MethodParameter methodParameter, + Function valueExtractor) throws Exception { + + Object value = createResolver() + .resolveArgument(methodParameter, this.bindContext, postForm("name=Enigma")) + .block(Duration.ZERO); + + NonBindingPojo nonBindingPojo = valueExtractor.apply(value); + assertThat(nonBindingPojo).isNotNull(); + assertThat(nonBindingPojo.getName()).isNull(); - assertThat(this.bindContext.getModel().asMap().get("foo")).isSameAs(foo); + String bindingResultKey = BindingResult.MODEL_KEY_PREFIX + modelKey; + + Map model = bindContext.getModel().asMap(); + assertThat(model).hasSize(2); + assertThat(model.get(modelKey)).isSameAs(nonBindingPojo); + assertThat(model.get(bindingResultKey)).isInstanceOf(BindingResult.class); } @Test - public void bindExistingSingle() throws Exception { - Foo foo = new Foo(); - foo.setName("Jim"); - this.bindContext.getModel().addAttribute("fooSingle", Single.just(foo)); - - MethodParameter parameter = this.testMethod.annotNotPresent(ModelAttribute.class).arg(Foo.class); - testBindFoo("foo", parameter, value -> { - assertThat(value.getClass()).isEqualTo(Foo.class); - return (Foo) value; + void bindExisting() throws Exception { + Pojo pojo = new Pojo(); + pojo.setName("Jim"); + this.bindContext.getModel().addAttribute(pojo); + + MethodParameter parameter = this.testMethod.annotNotPresent(ModelAttribute.class).arg(Pojo.class); + testBindPojo("pojo", parameter, value -> { + assertThat(value.getClass()).isEqualTo(Pojo.class); + return (Pojo) value; }); - assertThat(this.bindContext.getModel().asMap().get("foo")).isSameAs(foo); + assertThat(this.bindContext.getModel().asMap().get("pojo")).isSameAs(pojo); } @Test - public void bindExistingMonoToMono() throws Exception { - Foo foo = new Foo(); - foo.setName("Jim"); - String modelKey = "fooMono"; - this.bindContext.getModel().addAttribute(modelKey, Mono.just(foo)); + void bindExistingMono() throws Exception { + Pojo pojo = new Pojo(); + pojo.setName("Jim"); + this.bindContext.getModel().addAttribute("pojoMono", Mono.just(pojo)); + + MethodParameter parameter = this.testMethod.annotNotPresent(ModelAttribute.class).arg(Pojo.class); + testBindPojo("pojo", parameter, value -> { + assertThat(value.getClass()).isEqualTo(Pojo.class); + return (Pojo) value; + }); + + assertThat(this.bindContext.getModel().asMap().get("pojo")).isSameAs(pojo); + } + + @Test + void bindExistingSingle() throws Exception { + Pojo pojo = new Pojo(); + pojo.setName("Jim"); + this.bindContext.getModel().addAttribute("pojoSingle", Single.just(pojo)); + + MethodParameter parameter = this.testMethod.annotNotPresent(ModelAttribute.class).arg(Pojo.class); + testBindPojo("pojo", parameter, value -> { + assertThat(value.getClass()).isEqualTo(Pojo.class); + return (Pojo) value; + }); + + assertThat(this.bindContext.getModel().asMap().get("pojo")).isSameAs(pojo); + } + + @Test + void bindExistingMonoToMono() throws Exception { + Pojo pojo = new Pojo(); + pojo.setName("Jim"); + String modelKey = "pojoMono"; + this.bindContext.getModel().addAttribute(modelKey, Mono.just(pojo)); MethodParameter parameter = this.testMethod - .annotNotPresent(ModelAttribute.class).arg(Mono.class, Foo.class); + .annotNotPresent(ModelAttribute.class).arg(Mono.class, Pojo.class); - testBindFoo(modelKey, parameter, mono -> { - boolean condition = mono instanceof Mono; - assertThat(condition).as(mono.getClass().getName()).isTrue(); + testBindPojo(modelKey, parameter, mono -> { + assertThat(mono).isInstanceOf(Mono.class); Object value = ((Mono) mono).block(Duration.ofSeconds(5)); - assertThat(value.getClass()).isEqualTo(Foo.class); - return (Foo) value; + assertThat(value.getClass()).isEqualTo(Pojo.class); + return (Pojo) value; }); } - private void testBindFoo(String modelKey, MethodParameter param, Function valueExtractor) + private void testBindPojo(String modelKey, MethodParameter param, Function valueExtractor) throws Exception { Object value = createResolver() .resolveArgument(param, this.bindContext, postForm("name=Robert&age=25")) .block(Duration.ZERO); - Foo foo = valueExtractor.apply(value); - assertThat(foo.getName()).isEqualTo("Robert"); - assertThat(foo.getAge()).isEqualTo(25); + Pojo pojo = valueExtractor.apply(value); + assertThat(pojo.getName()).isEqualTo("Robert"); + assertThat(pojo.getAge()).isEqualTo(25); String bindingResultKey = BindingResult.MODEL_KEY_PREFIX + modelKey; - Map map = bindContext.getModel().asMap(); - assertThat(map.size()).as(map.toString()).isEqualTo(2); - assertThat(map.get(modelKey)).isSameAs(foo); - assertThat(map.get(bindingResultKey)).isNotNull(); - boolean condition = map.get(bindingResultKey) instanceof BindingResult; - assertThat(condition).isTrue(); + Map model = bindContext.getModel().asMap(); + assertThat(model).hasSize(2); + assertThat(model.get(modelKey)).isSameAs(pojo); + assertThat(model.get(bindingResultKey)).isInstanceOf(BindingResult.class); } @Test - public void validationError() throws Exception { - MethodParameter parameter = this.testMethod.annotNotPresent(ModelAttribute.class).arg(Foo.class); + void validationErrorForPojo() throws Exception { + MethodParameter parameter = this.testMethod.annotNotPresent(ModelAttribute.class).arg(Pojo.class); testValidationError(parameter, Function.identity()); } @Test - public void validationErrorToMono() throws Exception { + void validationErrorForMono() throws Exception { MethodParameter parameter = this.testMethod - .annotNotPresent(ModelAttribute.class).arg(Mono.class, Foo.class); + .annotNotPresent(ModelAttribute.class).arg(Mono.class, Pojo.class); testValidationError(parameter, resolvedArgumentMono -> { Object value = resolvedArgumentMono.block(Duration.ofSeconds(5)); - assertThat(value).isNotNull(); - boolean condition = value instanceof Mono; - assertThat(condition).isTrue(); + assertThat(value).isInstanceOf(Mono.class); return (Mono) value; }); } @Test - public void validationErrorToSingle() throws Exception { + void validationErrorForSingle() throws Exception { MethodParameter parameter = this.testMethod - .annotPresent(ModelAttribute.class).arg(Single.class, Foo.class); + .annotPresent(ModelAttribute.class).arg(Single.class, Pojo.class); testValidationError(parameter, resolvedArgumentMono -> { Object value = resolvedArgumentMono.block(Duration.ofSeconds(5)); - assertThat(value).isNotNull(); - boolean condition = value instanceof Single; - assertThat(condition).isTrue(); + assertThat(value).isInstanceOf(Single.class); return Mono.from(((Single) value).toFlowable()); }); } - private void testValidationError(MethodParameter param, Function, Mono> valueMonoExtractor) + @Test + void validationErrorWithoutBindingForPojo() throws Exception { + MethodParameter parameter = this.testMethod.annotPresent(ModelAttribute.class).arg(ValidatedPojo.class); + testValidationErrorWithoutBinding(parameter, Function.identity()); + } + + @Test + void validationErrorWithoutBindingForMono() throws Exception { + MethodParameter parameter = this.testMethod.annotPresent(ModelAttribute.class).arg(Mono.class, ValidatedPojo.class); + + testValidationErrorWithoutBinding(parameter, resolvedArgumentMono -> { + Object value = resolvedArgumentMono.block(Duration.ofSeconds(5)); + assertThat(value).isInstanceOf(Mono.class); + return (Mono) value; + }); + } + + @Test + void validationErrorWithoutBindingForSingle() throws Exception { + MethodParameter parameter = this.testMethod.annotPresent(ModelAttribute.class).arg(Single.class, ValidatedPojo.class); + + testValidationErrorWithoutBinding(parameter, resolvedArgumentMono -> { + Object value = resolvedArgumentMono.block(Duration.ofSeconds(5)); + assertThat(value).isInstanceOf(Single.class); + return Mono.from(((Single) value).toFlowable()); + }); + } + + private void testValidationError(MethodParameter parameter, Function, Mono> valueMonoExtractor) + throws URISyntaxException { + + testValidationError(parameter, valueMonoExtractor, "age=invalid", "age", "invalid"); + } + + private void testValidationErrorWithoutBinding(MethodParameter parameter, Function, Mono> valueMonoExtractor) throws URISyntaxException { - ServerWebExchange exchange = postForm("age=invalid"); - Mono mono = createResolver().resolveArgument(param, this.bindContext, exchange); + testValidationError(parameter, valueMonoExtractor, "name=Enigma", "name", null); + } + + private void testValidationError(MethodParameter param, Function, Mono> valueMonoExtractor, + String formData, String field, String rejectedValue) throws URISyntaxException { + + Mono mono = createResolver().resolveArgument(param, this.bindContext, postForm(formData)); mono = valueMonoExtractor.apply(mono); StepVerifier.create(mono) .consumeErrorWith(ex -> { - boolean condition = ex instanceof WebExchangeBindException; - assertThat(condition).isTrue(); + assertThat(ex).isInstanceOf(WebExchangeBindException.class); WebExchangeBindException bindException = (WebExchangeBindException) ex; assertThat(bindException.getErrorCount()).isEqualTo(1); - assertThat(bindException.hasFieldErrors("age")).isTrue(); + assertThat(bindException.hasFieldErrors(field)).isTrue(); + assertThat(bindException.getFieldError(field).getRejectedValue()).isEqualTo(rejectedValue); }) .verify(); } @Test - public void bindDataClass() throws Exception { - testBindBar(this.testMethod.annotNotPresent(ModelAttribute.class).arg(Bar.class)); - } + void bindDataClass() throws Exception { + MethodParameter parameter = this.testMethod.annotNotPresent(ModelAttribute.class).arg(DataClass.class); - private void testBindBar(MethodParameter param) throws Exception { Object value = createResolver() - .resolveArgument(param, this.bindContext, postForm("name=Robert&age=25&count=1")) + .resolveArgument(parameter, this.bindContext, postForm("name=Robert&age=25&count=1")) .block(Duration.ZERO); - Bar bar = (Bar) value; - assertThat(bar.getName()).isEqualTo("Robert"); - assertThat(bar.getAge()).isEqualTo(25); - assertThat(bar.getCount()).isEqualTo(1); + DataClass dataClass = (DataClass) value; + assertThat(dataClass.getName()).isEqualTo("Robert"); + assertThat(dataClass.getAge()).isEqualTo(25); + assertThat(dataClass.getCount()).isEqualTo(1); - String key = "bar"; - String bindingResultKey = BindingResult.MODEL_KEY_PREFIX + key; + String modelKey = "dataClass"; + String bindingResultKey = BindingResult.MODEL_KEY_PREFIX + modelKey; - Map map = bindContext.getModel().asMap(); - assertThat(map.size()).as(map.toString()).isEqualTo(2); - assertThat(map.get(key)).isSameAs(bar); - assertThat(map.get(bindingResultKey)).isNotNull(); - boolean condition = map.get(bindingResultKey) instanceof BindingResult; - assertThat(condition).isTrue(); + Map model = bindContext.getModel().asMap(); + assertThat(model).hasSize(2); + assertThat(model.get(modelKey)).isSameAs(dataClass); + assertThat(model.get(bindingResultKey)).isInstanceOf(BindingResult.class); } // TODO: SPR-15871, SPR-15542 @@ -320,31 +411,30 @@ public class ModelAttributeMethodArgumentResolverTests { @SuppressWarnings("unused") void handle( - @ModelAttribute @Validated Foo foo, - @ModelAttribute @Validated Mono mono, - @ModelAttribute @Validated Single single, - Foo fooNotAnnotated, + @ModelAttribute @Validated Pojo pojo, + @ModelAttribute @Validated Mono mono, + @ModelAttribute @Validated Single single, + @ModelAttribute(binding = false) NonBindingPojo nonBindingPojo, + @ModelAttribute(binding = false) Mono monoNonBindingPojo, + @ModelAttribute(binding = false) Single singleNonBindingPojo, + @ModelAttribute(binding = false) @Validated ValidatedPojo validatedPojo, + @ModelAttribute(binding = false) @Validated Mono monoValidatedPojo, + @ModelAttribute(binding = false) @Validated Single singleValidatedPojo, + Pojo pojoNotAnnotated, String stringNotAnnotated, - Mono monoNotAnnotated, + Mono monoNotAnnotated, Mono monoStringNotAnnotated, - Bar barNotAnnotated) { + DataClass dataClassNotAnnotated) { } @SuppressWarnings("unused") - private static class Foo { + private static class Pojo { private String name; private int age; - public Foo() { - } - - public Foo(String name) { - this.name = name; - } - public String getName() { return name; } @@ -364,7 +454,48 @@ public class ModelAttributeMethodArgumentResolverTests { @SuppressWarnings("unused") - private static class Bar { + private static class NonBindingPojo { + + private String name; + + public String getName() { + return this.name; + } + + public void setName(String name) { + this.name = name; + } + + @Override + public String toString() { + return "NonBindingPojo [name=" + name + "]"; + } + } + + + @SuppressWarnings("unused") + private static class ValidatedPojo { + + @NotEmpty + private String name; + + public String getName() { + return this.name; + } + + public void setName(String name) { + this.name = name; + } + + @Override + public String toString() { + return "ValidatedPojo [name=" + name + "]"; + } + } + + + @SuppressWarnings("unused") + private static class DataClass { private final String name; @@ -372,7 +503,7 @@ public class ModelAttributeMethodArgumentResolverTests { private int count; - public Bar(String name, int age) { + public DataClass(String name, int age) { this.name = name; this.age = age; }