From 9f229283fb4690aee347d22aae5f4625a8576746 Mon Sep 17 00:00:00 2001 From: Kazuki Shimizu Date: Wed, 14 Dec 2016 00:50:25 +0900 Subject: [PATCH] Support lazy initialization of BindingResult when customize a MessageCodesResolver Issue: SPR-15009 --- .../validation/DataBinder.java | 14 +++- .../validation/DataBinderTests.java | 72 +++++++++++++++++++ 2 files changed, 85 insertions(+), 1 deletion(-) diff --git a/spring-context/src/main/java/org/springframework/validation/DataBinder.java b/spring-context/src/main/java/org/springframework/validation/DataBinder.java index d32ca6fe79..cbdbc679fb 100644 --- a/spring-context/src/main/java/org/springframework/validation/DataBinder.java +++ b/spring-context/src/main/java/org/springframework/validation/DataBinder.java @@ -150,6 +150,8 @@ public class DataBinder implements PropertyEditorRegistry, TypeConverter { private ConversionService conversionService; + private MessageCodesResolver messageCodesResolver; + /** * Create a new DataBinder instance, with default object name. @@ -253,6 +255,9 @@ public class DataBinder implements PropertyEditorRegistry, TypeConverter { if (this.conversionService != null) { result.initConversion(this.conversionService); } + if (this.messageCodesResolver != null) { + result.setMessageCodesResolver(this.messageCodesResolver); + } return result; } @@ -279,6 +284,9 @@ public class DataBinder implements PropertyEditorRegistry, TypeConverter { if (this.conversionService != null) { result.initConversion(this.conversionService); } + if (this.messageCodesResolver != null) { + result.setMessageCodesResolver(this.messageCodesResolver); + } return result; } @@ -486,7 +494,11 @@ public class DataBinder implements PropertyEditorRegistry, TypeConverter { * @see DefaultMessageCodesResolver */ public void setMessageCodesResolver(MessageCodesResolver messageCodesResolver) { - getInternalBindingResult().setMessageCodesResolver(messageCodesResolver); + Assert.state(this.messageCodesResolver == null, "DataBinder is already initialized with MessageCodesResolver"); + this.messageCodesResolver = messageCodesResolver; + if (this.bindingResult != null && messageCodesResolver != null) { + this.bindingResult.setMessageCodesResolver(messageCodesResolver); + } } /** diff --git a/spring-context/src/test/java/org/springframework/validation/DataBinderTests.java b/spring-context/src/test/java/org/springframework/validation/DataBinderTests.java index a2a9fc8bcd..337d93f429 100644 --- a/spring-context/src/test/java/org/springframework/validation/DataBinderTests.java +++ b/spring-context/src/test/java/org/springframework/validation/DataBinderTests.java @@ -40,6 +40,7 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; +import org.springframework.beans.BeanWrapper; import org.springframework.beans.InvalidPropertyException; import org.springframework.beans.MutablePropertyValues; import org.springframework.beans.NotWritablePropertyException; @@ -2013,6 +2014,77 @@ public class DataBinderTests { binder.setAutoGrowCollectionLimit(257); } + @Test // SPR-15009 + public void testSetCustomMessageCodesResolverBeforeInitializeBindingResultForBeanPropertyAccess() { + TestBean testBean = new TestBean(); + DataBinder binder = new DataBinder(testBean, "testBean"); + DefaultMessageCodesResolver messageCodesResolver = new DefaultMessageCodesResolver(); + messageCodesResolver.setPrefix("errors."); + binder.setMessageCodesResolver(messageCodesResolver); + binder.setAutoGrowCollectionLimit(512); // allow configuration after set a MessageCodesResolver + binder.initBeanPropertyAccess(); + + MutablePropertyValues mpv = new MutablePropertyValues(); + mpv.add("age", "invalid"); + binder.bind(mpv); + assertEquals("errors.typeMismatch", binder.getBindingResult().getFieldError("age").getCode()); + assertEquals(512, BeanWrapper.class.cast(binder.getInternalBindingResult().getPropertyAccessor()).getAutoGrowCollectionLimit()); + } + + @Test // SPR-15009 + public void testSetCustomMessageCodesResolverBeforeInitializeBindingResultForDirectFieldAccess() { + TestBean testBean = new TestBean(); + DataBinder binder = new DataBinder(testBean, "testBean"); + DefaultMessageCodesResolver messageCodesResolver = new DefaultMessageCodesResolver(); + messageCodesResolver.setPrefix("errors."); + binder.setMessageCodesResolver(messageCodesResolver); + binder.initDirectFieldAccess(); + + MutablePropertyValues mpv = new MutablePropertyValues(); + mpv.add("age", "invalid"); + binder.bind(mpv); + assertEquals("errors.typeMismatch", binder.getBindingResult().getFieldError("age").getCode()); + } + + @Test // SPR-15009 + public void testSetCustomMessageCodesResolverAfterInitializeBindingResult() { + TestBean testBean = new TestBean(); + DataBinder binder = new DataBinder(testBean, "testBean"); + binder.initBeanPropertyAccess(); + DefaultMessageCodesResolver messageCodesResolver = new DefaultMessageCodesResolver(); + messageCodesResolver.setPrefix("errors."); + binder.setMessageCodesResolver(messageCodesResolver); + + MutablePropertyValues mpv = new MutablePropertyValues(); + mpv.add("age", "invalid"); + binder.bind(mpv); + assertEquals("errors.typeMismatch", binder.getBindingResult().getFieldError("age").getCode()); + } + + @Test // SPR-15009 + public void testSetMessageCodesResolverIsNullAfterInitializeBindingResult() { + TestBean testBean = new TestBean(); + DataBinder binder = new DataBinder(testBean, "testBean"); + binder.initBeanPropertyAccess(); + binder.setMessageCodesResolver(null); + + MutablePropertyValues mpv = new MutablePropertyValues(); + mpv.add("age", "invalid"); + binder.bind(mpv); + assertEquals("typeMismatch", binder.getBindingResult().getFieldError("age").getCode()); // Keep a default MessageCodesResolver + } + + @Test // SPR-15009 + public void testCallSetMessageCodesResolverTwice() { + expectedException.expect(IllegalStateException.class); + expectedException.expectMessage("DataBinder is already initialized with MessageCodesResolver"); + + TestBean testBean = new TestBean(); + DataBinder binder = new DataBinder(testBean, "testBean"); + binder.setMessageCodesResolver(new DefaultMessageCodesResolver()); + binder.setMessageCodesResolver(new DefaultMessageCodesResolver()); + + } @SuppressWarnings("unused") private static class BeanWithIntegerList {