001/*
002 * Copyright 2002-2017 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;
020import javax.validation.Validator;
021import javax.validation.ValidatorFactory;
022
023import org.aopalliance.aop.Advice;
024
025import org.springframework.aop.Pointcut;
026import org.springframework.aop.framework.autoproxy.AbstractBeanFactoryAwareAdvisingPostProcessor;
027import org.springframework.aop.support.DefaultPointcutAdvisor;
028import org.springframework.aop.support.annotation.AnnotationMatchingPointcut;
029import org.springframework.beans.factory.InitializingBean;
030import org.springframework.beans.factory.config.BeanPostProcessor;
031import org.springframework.util.Assert;
032import org.springframework.validation.annotation.Validated;
033
034/**
035 * A convenient {@link BeanPostProcessor} implementation that delegates to a
036 * JSR-303 provider for performing method-level validation on annotated methods.
037 *
038 * <p>Applicable methods have JSR-303 constraint annotations on their parameters
039 * and/or on their return value (in the latter case specified at the method level,
040 * typically as inline annotation), e.g.:
041 *
042 * <pre class="code">
043 * public @NotNull Object myValidMethod(@NotNull String arg1, @Max(10) int arg2)
044 * </pre>
045 *
046 * <p>Target classes with such annotated methods need to be annotated with Spring's
047 * {@link Validated} annotation at the type level, for their methods to be searched for
048 * inline constraint annotations. Validation groups can be specified through {@code @Validated}
049 * as well. By default, JSR-303 will validate against its default group only.
050 *
051 * <p>As of Spring 4.0, this functionality requires either a Bean Validation 1.1 provider
052 * (such as Hibernate Validator 5.x) or the Bean Validation 1.0 API with Hibernate Validator
053 * 4.3. The actual provider will be autodetected and automatically adapted.
054 *
055 * @author Juergen Hoeller
056 * @since 3.1
057 * @see MethodValidationInterceptor
058 * @see javax.validation.executable.ExecutableValidator
059 * @see org.hibernate.validator.method.MethodValidator
060 */
061@SuppressWarnings("serial")
062public class MethodValidationPostProcessor extends AbstractBeanFactoryAwareAdvisingPostProcessor
063                implements InitializingBean {
064
065        private Class<? extends Annotation> validatedAnnotationType = Validated.class;
066
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(Validator validator) {
126                return (validator != null ? new MethodValidationInterceptor(validator) : new MethodValidationInterceptor());
127        }
128
129}