001/*
002 * Copyright 2002-2020 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.lang.annotation.Annotation;
020
021import javax.validation.Validator;
022import javax.validation.ValidatorFactory;
023
024import org.aopalliance.aop.Advice;
025
026import org.springframework.aop.Pointcut;
027import org.springframework.aop.framework.autoproxy.AbstractBeanFactoryAwareAdvisingPostProcessor;
028import org.springframework.aop.support.DefaultPointcutAdvisor;
029import org.springframework.aop.support.annotation.AnnotationMatchingPointcut;
030import org.springframework.beans.factory.InitializingBean;
031import org.springframework.beans.factory.config.BeanPostProcessor;
032import org.springframework.lang.Nullable;
033import org.springframework.util.Assert;
034import org.springframework.validation.annotation.Validated;
035
036/**
037 * A convenient {@link BeanPostProcessor} implementation that delegates to a
038 * JSR-303 provider for performing method-level validation on annotated methods.
039 *
040 * <p>Applicable methods have JSR-303 constraint annotations on their parameters
041 * and/or on their return value (in the latter case specified at the method level,
042 * typically as inline annotation), e.g.:
043 *
044 * <pre class="code">
045 * public @NotNull Object myValidMethod(@NotNull String arg1, @Max(10) int arg2)
046 * </pre>
047 *
048 * <p>Target classes with such annotated methods need to be annotated with Spring's
049 * {@link Validated} annotation at the type level, for their methods to be searched for
050 * inline constraint annotations. Validation groups can be specified through {@code @Validated}
051 * as well. By default, JSR-303 will validate against its default group only.
052 *
053 * <p>As of Spring 5.0, this functionality requires a Bean Validation 1.1+ provider.
054 *
055 * @author Juergen Hoeller
056 * @since 3.1
057 * @see MethodValidationInterceptor
058 * @see javax.validation.executable.ExecutableValidator
059 */
060@SuppressWarnings("serial")
061public class MethodValidationPostProcessor extends AbstractBeanFactoryAwareAdvisingPostProcessor
062                implements InitializingBean {
063
064        private Class<? extends Annotation> validatedAnnotationType = Validated.class;
065
066        @Nullable
067        private Validator validator;
068
069
070        /**
071         * Set the 'validated' annotation type.
072         * The default validated annotation type is the {@link Validated} annotation.
073         * <p>This setter property exists so that developers can provide their own
074         * (non-Spring-specific) annotation type to indicate that a class is supposed
075         * to be validated in the sense of applying method validation.
076         * @param validatedAnnotationType the desired annotation type
077         */
078        public void setValidatedAnnotationType(Class<? extends Annotation> validatedAnnotationType) {
079                Assert.notNull(validatedAnnotationType, "'validatedAnnotationType' must not be null");
080                this.validatedAnnotationType = validatedAnnotationType;
081        }
082
083        /**
084         * Set the JSR-303 Validator to delegate to for validating methods.
085         * <p>Default is the default ValidatorFactory's default Validator.
086         */
087        public void setValidator(Validator validator) {
088                // Unwrap to the native Validator with forExecutables support
089                if (validator instanceof LocalValidatorFactoryBean) {
090                        this.validator = ((LocalValidatorFactoryBean) validator).getValidator();
091                }
092                else if (validator instanceof SpringValidatorAdapter) {
093                        this.validator = validator.unwrap(Validator.class);
094                }
095                else {
096                        this.validator = validator;
097                }
098        }
099
100        /**
101         * Set the JSR-303 ValidatorFactory to delegate to for validating methods,
102         * using its default Validator.
103         * <p>Default is the default ValidatorFactory's default Validator.
104         * @see javax.validation.ValidatorFactory#getValidator()
105         */
106        public void setValidatorFactory(ValidatorFactory validatorFactory) {
107                this.validator = validatorFactory.getValidator();
108        }
109
110
111        @Override
112        public void afterPropertiesSet() {
113                Pointcut pointcut = new AnnotationMatchingPointcut(this.validatedAnnotationType, true);
114                this.advisor = new DefaultPointcutAdvisor(pointcut, createMethodValidationAdvice(this.validator));
115        }
116
117        /**
118         * Create AOP advice for method validation purposes, to be applied
119         * with a pointcut for the specified 'validated' annotation.
120         * @param validator the JSR-303 Validator to delegate to
121         * @return the interceptor to use (typically, but not necessarily,
122         * a {@link MethodValidationInterceptor} or subclass thereof)
123         * @since 4.2
124         */
125        protected Advice createMethodValidationAdvice(@Nullable Validator validator) {
126                return (validator != null ? new MethodValidationInterceptor(validator) : new MethodValidationInterceptor());
127        }
128
129}