/** * A convenient {@link BeanPostProcessor} implementation that delegates to a * JSR-303 provider for performing method-level validation on annotated methods. * * <p>Applicable methods have JSR-303 constraint annotations on their parameters * and/or on their return value (in the latter case specified at the method level, * typically as inline annotation), e.g.: * * <pre class="code"> * public @NotNull Object myValidMethod(@NotNull String arg1, @Max(10) int arg2) * </pre> * * <p>Target classes with such annotated methods need to be annotated with Spring's * {@link Validated} annotation at the type level, for their methods to be searched for * inline constraint annotations. Validation groups can be specified through {@code @Validated} * as well. By default, JSR-303 will validate against its default group only. * * <p>As of Spring 5.0, this functionality requires a Bean Validation 1.1 provider. * * @author Juergen Hoeller * @since 3.1 * @see MethodValidationInterceptor * @see javax.validation.executable.ExecutableValidator */ @SuppressWarnings("serial") public class MethodValidationPostProcessor extends AbstractBeanFactoryAwareAdvisingPostProcessor implements InitializingBean {
/** * Set the 'validated' annotation type. * The default validated annotation type is the {@link Validated} annotation. * <p>This setter property exists so that developers can provide their own * (non-Spring-specific) annotation type to indicate that a class is supposed * to be validated in the sense of applying method validation. * @param validatedAnnotationType the desired annotation type */ public void setValidatedAnnotationType(Class<? extends Annotation> validatedAnnotationType) { Assert.notNull(validatedAnnotationType, "'validatedAnnotationType' must not be null"); this.validatedAnnotationType = validatedAnnotationType; }
/** * Set the JSR-303 Validator to delegate to for validating methods. * <p>Default is the default ValidatorFactory's default Validator. */ public void setValidator(Validator validator) { // Unwrap to the native Validator with forExecutables support if (validator instanceof LocalValidatorFactoryBean) { this.validator = ((LocalValidatorFactoryBean) validator).getValidator(); } else if (validator instanceof SpringValidatorAdapter) { this.validator = validator.unwrap(Validator.class); } else { this.validator = validator; } }
/** * Set the JSR-303 ValidatorFactory to delegate to for validating methods, * 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(); }
@Override public void afterPropertiesSet() { Pointcut pointcut = new AnnotationMatchingPointcut(this.validatedAnnotationType, true); this.advisor = new DefaultPointcutAdvisor(pointcut, createMethodValidationAdvice(this.validator)); }
/** * Create AOP advice for method validation purposes, to be applied * with a pointcut for the specified 'validated' annotation. * @param validator the JSR-303 Validator to delegate to * @return the interceptor to use (typically, but not necessarily, * a {@link MethodValidationInterceptor} or subclass thereof) * @since 4.2 */ protected Advice createMethodValidationAdvice(@Nullable Validator validator) { return (validator != null ? new MethodValidationInterceptor(validator) : new MethodValidationInterceptor()); }
@Override @SuppressWarnings("unchecked") public Object invoke(MethodInvocation invocation) throws Throwable { // Avoid Validator invocation on FactoryBean.getObjectType/isSingleton if (isFactoryBeanMetadataMethod(invocation.getMethod())) { return invocation.proceed(); }
Class<?>[] groups = determineValidationGroups(invocation);
// Standard Bean Validation 1.1 API ExecutableValidator execVal = this.validator.forExecutables(); Method methodToValidate = invocation.getMethod(); Set<ConstraintViolation<Object>> result;
try { result = execVal.validateParameters( invocation.getThis(), methodToValidate, invocation.getArguments(), groups); } catch (IllegalArgumentException ex) { // Probably a generic type mismatch between interface and impl as reported in SPR-12237 / HV-1011 // Let's try to find the bridged method on the implementation class... methodToValidate = BridgeMethodResolver.findBridgedMethod( ClassUtils.getMostSpecificMethod(invocation.getMethod(), invocation.getThis().getClass())); result = execVal.validateParameters( invocation.getThis(), methodToValidate, invocation.getArguments(), groups); } if (!result.isEmpty()) { throw new ConstraintViolationException(result); }
Object returnValue = invocation.proceed();
result = execVal.validateReturnValue(invocation.getThis(), methodToValidate, returnValue, groups); if (!result.isEmpty()) { throw new ConstraintViolationException(result); }
List<ValidationProvider<?>> validationProviders; try { validationProviders = resolver.getValidationProviders(); } // don't wrap existing ValidationExceptions in another ValidationException catch ( ValidationException e ) { throw e; } // if any other exception occurs wrap it in a ValidationException catch ( RuntimeException re ) { throw new ValidationException( "Unable to get available provider resolvers.", re ); }
if ( validationProviders.isEmpty() ) { String msg = "Unable to create a Configuration, because no Bean Validation provider could be found." + " Add a provider like Hibernate Validator (RI) to your classpath."; throw new NoProviderFoundException( msg ); }
Configuration<?> config; try { config = resolver.getValidationProviders().get( 0 ).createGenericConfiguration( this ); } catch ( RuntimeException re ) { throw new ValidationException( "Unable to instantiate Configuration.", re ); }
private List<ValidationProvider<?>> loadProviders(ClassLoader classloader) { ServiceLoader<ValidationProvider> loader = ServiceLoader.load( ValidationProvider.class, classloader ); Iterator<ValidationProvider> providerIterator = loader.iterator(); List<ValidationProvider<?>> validationProviderList = new ArrayList<>(); while ( providerIterator.hasNext() ) { try { validationProviderList.add( providerIterator.next() ); } catch ( ServiceConfigurationError e ) { // ignore, because it can happen when multiple // providers are present and some of them are not class loader // compatible with our API. } } return validationProviderList; }
public interface ConstraintValidator<A extends Annotation, T> {
/** * Initializes the validator in preparation for * {@link #isValid(Object, ConstraintValidatorContext)} calls. * The constraint annotation for a given constraint declaration * is passed. * <p> * This method is guaranteed to be called before any use of this instance for * validation. * <p> * The default implementation is a no-op. * * @param constraintAnnotation annotation instance for a given constraint declaration */ default void initialize(A constraintAnnotation) { }
/** * Implements the validation logic. * The state of {@code value} must not be altered. * <p> * This method can be accessed concurrently, thread-safety must be ensured * by the implementation. * * @param value object to validate * @param context context in which the constraint is evaluated * * @return {@code false} if {@code value} does not pass the constraint */ boolean isValid(T value, ConstraintValidatorContext context); }