All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.springframework.validation.beanvalidation.MethodValidationInterceptor Maven / Gradle / Ivy

There is a newer version: 6.1.6
Show newest version
/*
 * Copyright 2002-2022 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
 *
 *      https://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 java.lang.reflect.Method;
import java.util.Set;
import java.util.function.Supplier;

import jakarta.validation.ConstraintViolation;
import jakarta.validation.ConstraintViolationException;
import jakarta.validation.Validation;
import jakarta.validation.Validator;
import jakarta.validation.ValidatorFactory;
import jakarta.validation.executable.ExecutableValidator;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.SmartFactoryBean;
import org.springframework.core.BridgeMethodResolver;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.function.SingletonSupplier;
import org.springframework.validation.annotation.Validated;

/**
 * An AOP Alliance {@link MethodInterceptor} implementation that delegates to a
 * JSR-303 provider for performing method-level validation on annotated methods.
 *
 * 

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.: {@code public @NotNull Object myValidMethod(@NotNull String arg1, @Max(10) int arg2)} * *

Validation groups can be specified through Spring's {@link Validated} annotation * at the type level of the containing target class, applying to all public service methods * of that class. By default, JSR-303 will validate against its default group only. * *

As of Spring 5.0, this functionality requires a Bean Validation 1.1+ provider. * * @author Juergen Hoeller * @since 3.1 * @see MethodValidationPostProcessor * @see jakarta.validation.executable.ExecutableValidator */ public class MethodValidationInterceptor implements MethodInterceptor { private final Supplier validator; /** * Create a new MethodValidationInterceptor using a default JSR-303 validator underneath. */ public MethodValidationInterceptor() { this.validator = SingletonSupplier.of(() -> Validation.buildDefaultValidatorFactory().getValidator()); } /** * Create a new MethodValidationInterceptor using the given JSR-303 ValidatorFactory. * @param validatorFactory the JSR-303 ValidatorFactory to use */ public MethodValidationInterceptor(ValidatorFactory validatorFactory) { this.validator = SingletonSupplier.of(validatorFactory::getValidator); } /** * Create a new MethodValidationInterceptor using the given JSR-303 Validator. * @param validator the JSR-303 Validator to use */ public MethodValidationInterceptor(Validator validator) { this.validator = () -> validator; } /** * Create a new MethodValidationInterceptor for the supplied * (potentially lazily initialized) Validator. * @param validator a Supplier for the Validator to use * @since 6.0 */ public MethodValidationInterceptor(Supplier validator) { this.validator = validator; } @Override @Nullable 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.get().forExecutables(); Method methodToValidate = invocation.getMethod(); Set> result; Object target = invocation.getThis(); Assert.state(target != null, "Target must not be null"); try { result = execVal.validateParameters(target, 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(), target.getClass())); result = execVal.validateParameters(target, methodToValidate, invocation.getArguments(), groups); } if (!result.isEmpty()) { throw new ConstraintViolationException(result); } Object returnValue = invocation.proceed(); result = execVal.validateReturnValue(target, methodToValidate, returnValue, groups); if (!result.isEmpty()) { throw new ConstraintViolationException(result); } return returnValue; } private boolean isFactoryBeanMetadataMethod(Method method) { Class clazz = method.getDeclaringClass(); // Call from interface-based proxy handle, allowing for an efficient check? if (clazz.isInterface()) { return ((clazz == FactoryBean.class || clazz == SmartFactoryBean.class) && !method.getName().equals("getObject")); } // Call from CGLIB proxy handle, potentially implementing a FactoryBean method? Class factoryBeanType = null; if (SmartFactoryBean.class.isAssignableFrom(clazz)) { factoryBeanType = SmartFactoryBean.class; } else if (FactoryBean.class.isAssignableFrom(clazz)) { factoryBeanType = FactoryBean.class; } return (factoryBeanType != null && !method.getName().equals("getObject") && ClassUtils.hasMethod(factoryBeanType, method)); } /** * Determine the validation groups to validate against for the given method invocation. *

Default are the validation groups as specified in the {@link Validated} annotation * on the containing target class of the method. * @param invocation the current MethodInvocation * @return the applicable validation groups as a Class array */ protected Class[] determineValidationGroups(MethodInvocation invocation) { Validated validatedAnn = AnnotationUtils.findAnnotation(invocation.getMethod(), Validated.class); if (validatedAnn == null) { Object target = invocation.getThis(); Assert.state(target != null, "Target must not be null"); validatedAnn = AnnotationUtils.findAnnotation(target.getClass(), Validated.class); } return (validatedAnn != null ? validatedAnn.value() : new Class[0]); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy