diff --git a/org.springframework.context/src/main/java/org/springframework/validation/beanvalidation/BeanValidationPostProcessor.java b/org.springframework.context/src/main/java/org/springframework/validation/beanvalidation/BeanValidationPostProcessor.java
index be9d84edfa3..3edc4581a49 100644
--- a/org.springframework.context/src/main/java/org/springframework/validation/beanvalidation/BeanValidationPostProcessor.java
+++ b/org.springframework.context/src/main/java/org/springframework/validation/beanvalidation/BeanValidationPostProcessor.java
@@ -29,33 +29,77 @@ import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.config.BeanPostProcessor;
/**
+ * Simple {@link BeanPostProcessor} that checks JSR-303 constraint annotations
+ * in Spring-managed beans, throwing an initialization exception in case of
+ * constraint violations right before calling the bean's init method (if any).
+ *
* @author Juergen Hoeller
* @since 3.0
*/
public class BeanValidationPostProcessor implements BeanPostProcessor, InitializingBean {
- private javax.validation.Validator validator;
+ private Validator validator;
+
+ private boolean afterInitialization = false;
+ /**
+ * Set the JSR-303 Validator to delegate to for validating beans.
+ *
Default is the default ValidatorFactory's default Validator.
+ */
public void setValidator(Validator validator) {
this.validator = validator;
}
+ /**
+ * Set the JSR-303 ValidatorFactory to delegate to for validating beans,
+ * using its default Validator.
+ *
Default is the default ValidatorFactory's default Validator.
+ * @see javax.validation.ValidatorFactory#getValidator()
+ */
public void setValidatorFactory(ValidatorFactory validatorFactory) {
this.validator = validatorFactory.getValidator();
}
+ /**
+ * Choose whether to perform validation after bean initialization
+ * (i.e. after init methods) instead of before (which is the default).
+ *
Default is "false" (before initialization). Switch this to "true"
+ * (after initialization) if you would like to give init methods a chance
+ * to populate constrained fields before they get validated.
+ */
+ public void setAfterInitialization(boolean afterInitialization) {
+ this.afterInitialization = afterInitialization;
+ }
+
public void afterPropertiesSet() {
if (this.validator == null) {
- Validation.buildDefaultValidatorFactory().getValidator();
+ this.validator = Validation.buildDefaultValidatorFactory().getValidator();
}
}
+
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
+ if (!this.afterInitialization) {
+ doValidate(bean);
+ }
return bean;
}
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
+ if (this.afterInitialization) {
+ doValidate(bean);
+ }
+ return bean;
+ }
+
+
+ /**
+ * Perform validation of the given bean.
+ * @param bean the bean instance to validate
+ * @see javax.validation.Validator#validate
+ */
+ protected void doValidate(Object bean) {
Set> result = this.validator.validate(bean);
if (!result.isEmpty()) {
StringBuilder sb = new StringBuilder("Bean state is invalid: ");
@@ -68,7 +112,6 @@ public class BeanValidationPostProcessor implements BeanPostProcessor, Initializ
}
throw new BeanInitializationException(sb.toString());
}
- return bean;
}
}
diff --git a/org.springframework.context/src/test/java/org/springframework/validation/beanvalidation/BeanValidationPostProcessorTests.java b/org.springframework.context/src/test/java/org/springframework/validation/beanvalidation/BeanValidationPostProcessorTests.java
new file mode 100644
index 00000000000..42ca119e736
--- /dev/null
+++ b/org.springframework.context/src/test/java/org/springframework/validation/beanvalidation/BeanValidationPostProcessorTests.java
@@ -0,0 +1,156 @@
+/*
+ * 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.validation.beanvalidation;
+
+import javax.annotation.PostConstruct;
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Size;
+
+import static org.junit.Assert.*;
+import org.junit.Test;
+
+import org.springframework.beans.TestBean;
+import org.springframework.beans.factory.BeanCreationException;
+import org.springframework.beans.factory.support.RootBeanDefinition;
+import org.springframework.context.annotation.CommonAnnotationBeanPostProcessor;
+import org.springframework.context.support.GenericApplicationContext;
+
+/**
+ * @author Juergen Hoeller
+ * @since 3.0
+ */
+public class BeanValidationPostProcessorTests {
+
+ @Test
+ public void testNotNullConstraint() {
+ GenericApplicationContext ac = new GenericApplicationContext();
+ ac.registerBeanDefinition("bvpp", new RootBeanDefinition(BeanValidationPostProcessor.class));
+ ac.registerBeanDefinition("capp", new RootBeanDefinition(CommonAnnotationBeanPostProcessor.class));
+ ac.registerBeanDefinition("bean", new RootBeanDefinition(NotNullConstrainedBean.class));
+ try {
+ ac.refresh();
+ fail("Should have thrown BeanCreationException");
+ }
+ catch (BeanCreationException ex) {
+ assertTrue(ex.getRootCause().getMessage().contains("testBean"));
+ assertTrue(ex.getRootCause().getMessage().contains("invalid"));
+ }
+ }
+
+ @Test
+ public void testNotNullConstraintSatisfied() {
+ GenericApplicationContext ac = new GenericApplicationContext();
+ ac.registerBeanDefinition("bvpp", new RootBeanDefinition(BeanValidationPostProcessor.class));
+ ac.registerBeanDefinition("capp", new RootBeanDefinition(CommonAnnotationBeanPostProcessor.class));
+ RootBeanDefinition bd = new RootBeanDefinition(NotNullConstrainedBean.class);
+ bd.getPropertyValues().add("testBean", new TestBean());
+ ac.registerBeanDefinition("bean", bd);
+ ac.refresh();
+ }
+
+ @Test
+ public void testNotNullConstraintAfterInitialization() {
+ GenericApplicationContext ac = new GenericApplicationContext();
+ RootBeanDefinition bvpp = new RootBeanDefinition(BeanValidationPostProcessor.class);
+ bvpp.getPropertyValues().add("afterInitialization", true);
+ ac.registerBeanDefinition("bvpp", bvpp);
+ ac.registerBeanDefinition("capp", new RootBeanDefinition(CommonAnnotationBeanPostProcessor.class));
+ ac.registerBeanDefinition("bean", new RootBeanDefinition(AfterInitConstraintBean.class));
+ ac.refresh();
+ }
+
+ @Test
+ public void testSizeConstraint() {
+ GenericApplicationContext ac = new GenericApplicationContext();
+ ac.registerBeanDefinition("bvpp", new RootBeanDefinition(BeanValidationPostProcessor.class));
+ RootBeanDefinition bd = new RootBeanDefinition(NotNullConstrainedBean.class);
+ bd.getPropertyValues().add("testBean", new TestBean());
+ bd.getPropertyValues().add("stringValue", "s");
+ ac.registerBeanDefinition("bean", bd);
+ try {
+ ac.refresh();
+ fail("Should have thrown BeanCreationException");
+ }
+ catch (BeanCreationException ex) {
+ assertTrue(ex.getRootCause().getMessage().contains("stringValue"));
+ assertTrue(ex.getRootCause().getMessage().contains("invalid"));
+ }
+ }
+
+ @Test
+ public void testSizeConstraintSatisfied() {
+ GenericApplicationContext ac = new GenericApplicationContext();
+ ac.registerBeanDefinition("bvpp", new RootBeanDefinition(BeanValidationPostProcessor.class));
+ RootBeanDefinition bd = new RootBeanDefinition(NotNullConstrainedBean.class);
+ bd.getPropertyValues().add("testBean", new TestBean());
+ bd.getPropertyValues().add("stringValue", "ss");
+ ac.registerBeanDefinition("bean", bd);
+ ac.refresh();
+ }
+
+
+ public static class NotNullConstrainedBean {
+
+ @NotNull
+ private TestBean testBean;
+
+ @Size(min = 2)
+ private String stringValue;
+
+ public TestBean getTestBean() {
+ return testBean;
+ }
+
+ public void setTestBean(TestBean testBean) {
+ this.testBean = testBean;
+ }
+
+ public String getStringValue() {
+ return stringValue;
+ }
+
+ public void setStringValue(String stringValue) {
+ this.stringValue = stringValue;
+ }
+
+ @PostConstruct
+ public void init() {
+ assertNotNull("Shouldn't be here after constraint checking", this.testBean);
+ }
+ }
+
+
+ public static class AfterInitConstraintBean {
+
+ @NotNull
+ private TestBean testBean;
+
+ public TestBean getTestBean() {
+ return testBean;
+ }
+
+ public void setTestBean(TestBean testBean) {
+ this.testBean = testBean;
+ }
+
+ @PostConstruct
+ public void init() {
+ this.testBean = new TestBean();
+ }
+ }
+
+}