Browse Source

BeanValidationPostProcessor runs in before-initialization phase by default (SPR-6565)

pull/23217/head
Juergen Hoeller 15 years ago
parent
commit
d6f4f4c7b4
  1. 49
      org.springframework.context/src/main/java/org/springframework/validation/beanvalidation/BeanValidationPostProcessor.java
  2. 156
      org.springframework.context/src/test/java/org/springframework/validation/beanvalidation/BeanValidationPostProcessorTests.java

49
org.springframework.context/src/main/java/org/springframework/validation/beanvalidation/BeanValidationPostProcessor.java

@ -29,33 +29,77 @@ import org.springframework.beans.factory.InitializingBean; @@ -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.
* <p>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.
* <p>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).
* <p>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<ConstraintViolation<Object>> 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 @@ -68,7 +112,6 @@ public class BeanValidationPostProcessor implements BeanPostProcessor, Initializ
}
throw new BeanInitializationException(sb.toString());
}
return bean;
}
}

156
org.springframework.context/src/test/java/org/springframework/validation/beanvalidation/BeanValidationPostProcessorTests.java

@ -0,0 +1,156 @@ @@ -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();
}
}
}
Loading…
Cancel
Save