001/* 002 * Copyright 2002-2018 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.validation.beanvalidation; 018 019import java.util.Iterator; 020import java.util.Set; 021 022import javax.validation.ConstraintViolation; 023import javax.validation.Validation; 024import javax.validation.Validator; 025import javax.validation.ValidatorFactory; 026 027import org.springframework.aop.framework.AopProxyUtils; 028import org.springframework.beans.BeansException; 029import org.springframework.beans.factory.BeanInitializationException; 030import org.springframework.beans.factory.InitializingBean; 031import org.springframework.beans.factory.config.BeanPostProcessor; 032import org.springframework.lang.Nullable; 033import org.springframework.util.Assert; 034 035/** 036 * Simple {@link BeanPostProcessor} that checks JSR-303 constraint annotations 037 * in Spring-managed beans, throwing an initialization exception in case of 038 * constraint violations right before calling the bean's init method (if any). 039 * 040 * @author Juergen Hoeller 041 * @since 3.0 042 */ 043public class BeanValidationPostProcessor implements BeanPostProcessor, InitializingBean { 044 045 @Nullable 046 private Validator validator; 047 048 private boolean afterInitialization = false; 049 050 051 /** 052 * Set the JSR-303 Validator to delegate to for validating beans. 053 * <p>Default is the default ValidatorFactory's default Validator. 054 */ 055 public void setValidator(Validator validator) { 056 this.validator = validator; 057 } 058 059 /** 060 * Set the JSR-303 ValidatorFactory to delegate to for validating beans, 061 * using its default Validator. 062 * <p>Default is the default ValidatorFactory's default Validator. 063 * @see javax.validation.ValidatorFactory#getValidator() 064 */ 065 public void setValidatorFactory(ValidatorFactory validatorFactory) { 066 this.validator = validatorFactory.getValidator(); 067 } 068 069 /** 070 * Choose whether to perform validation after bean initialization 071 * (i.e. after init methods) instead of before (which is the default). 072 * <p>Default is "false" (before initialization). Switch this to "true" 073 * (after initialization) if you would like to give init methods a chance 074 * to populate constrained fields before they get validated. 075 */ 076 public void setAfterInitialization(boolean afterInitialization) { 077 this.afterInitialization = afterInitialization; 078 } 079 080 @Override 081 public void afterPropertiesSet() { 082 if (this.validator == null) { 083 this.validator = Validation.buildDefaultValidatorFactory().getValidator(); 084 } 085 } 086 087 088 @Override 089 public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { 090 if (!this.afterInitialization) { 091 doValidate(bean); 092 } 093 return bean; 094 } 095 096 @Override 097 public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { 098 if (this.afterInitialization) { 099 doValidate(bean); 100 } 101 return bean; 102 } 103 104 105 /** 106 * Perform validation of the given bean. 107 * @param bean the bean instance to validate 108 * @see javax.validation.Validator#validate 109 */ 110 protected void doValidate(Object bean) { 111 Assert.state(this.validator != null, "No Validator set"); 112 Object objectToValidate = AopProxyUtils.getSingletonTarget(bean); 113 if (objectToValidate == null) { 114 objectToValidate = bean; 115 } 116 Set<ConstraintViolation<Object>> result = this.validator.validate(objectToValidate); 117 118 if (!result.isEmpty()) { 119 StringBuilder sb = new StringBuilder("Bean state is invalid: "); 120 for (Iterator<ConstraintViolation<Object>> it = result.iterator(); it.hasNext();) { 121 ConstraintViolation<Object> violation = it.next(); 122 sb.append(violation.getPropertyPath()).append(" - ").append(violation.getMessage()); 123 if (it.hasNext()) { 124 sb.append("; "); 125 } 126 } 127 throw new BeanInitializationException(sb.toString()); 128 } 129 } 130 131}