Skip to content

Commit e648245

Browse files
committed
added MethodValidationInterceptor/PostProcessor for Hibernate Validator 4.2 based method validation; renamed Spring's variant of @Valid to @validated
1 parent 2dba480 commit e648245

File tree

11 files changed

+455
-68
lines changed

11 files changed

+455
-68
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,56 +1,63 @@
1-
/*
2-
* Copyright 2002-2011 the original author or authors.
3-
*
4-
* Licensed under the Apache License, Version 2.0 (the "License");
5-
* you may not use this file except in compliance with the License.
6-
* You may obtain a copy of the License at
7-
*
8-
* http://www.apache.org/licenses/LICENSE-2.0
9-
*
10-
* Unless required by applicable law or agreed to in writing, software
11-
* distributed under the License is distributed on an "AS IS" BASIS,
12-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13-
* See the License for the specific language governing permissions and
14-
* limitations under the License.
15-
*/
16-
17-
package org.springframework.validation.annotation;
18-
19-
import java.lang.annotation.Documented;
20-
import java.lang.annotation.ElementType;
21-
import java.lang.annotation.Retention;
22-
import java.lang.annotation.RetentionPolicy;
23-
import java.lang.annotation.Target;
24-
25-
/**
26-
* Extended variant of JSR-303's {@link javax.validation.Valid},
27-
* supporting the specification of validation groups. Designed for
28-
* convenient use with Spring's JSR-303 support but not JSR-303 specific.
29-
*
30-
* <p>Can be used e.g. with Spring MVC handler methods arguments.
31-
* Supported through {@link org.springframework.validation.SmartValidator}'s
32-
* validation hint concept, with validation group classes acting as hint objects.
33-
*
34-
* @author Juergen Hoeller
35-
* @since 3.1
36-
* @see javax.validation.Validator#validate(Object, Class[])
37-
* @see org.springframework.validation.SmartValidator#validate(Object, org.springframework.validation.Errors, Object...)
38-
* @see org.springframework.validation.beanvalidation.SpringValidatorAdapter
39-
*/
40-
@Target(ElementType.PARAMETER)
41-
@Retention(RetentionPolicy.RUNTIME)
42-
@Documented
43-
public @interface Valid {
44-
45-
/**
46-
* Specify one or more validation groups to apply to the validation step
47-
* kicked off by this annotation.
48-
* <p>JSR-303 defines validation groups as custom annotations which an application declares
49-
* for the sole purpose of using them as type-safe group arguments, as implemented in
50-
* {@link org.springframework.validation.beanvalidation.SpringValidatorAdapter}.
51-
* <p>Other {@link org.springframework.validation.SmartValidator} implementations may
52-
* support class arguments in other ways as well.
53-
*/
54-
Class[] value() default {};
55-
56-
}
1+
/*
2+
* Copyright 2002-2011 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.validation.annotation;
18+
19+
import java.lang.annotation.Documented;
20+
import java.lang.annotation.ElementType;
21+
import java.lang.annotation.Retention;
22+
import java.lang.annotation.RetentionPolicy;
23+
import java.lang.annotation.Target;
24+
25+
/**
26+
* Variant of JSR-303's {@link javax.validation.Valid}, supporting the
27+
* specification of validation groups. Designed for convenient use with
28+
* Spring's JSR-303 support but not JSR-303 specific.
29+
*
30+
* <p>Can be used e.g. with Spring MVC handler methods arguments.
31+
* Supported through {@link org.springframework.validation.SmartValidator}'s
32+
* validation hint concept, with validation group classes acting as hint objects.
33+
*
34+
* <p>Can also be used with method level validation, indicating that a specific
35+
* class is supposed to be validated at the method level (acting as a pointcut
36+
* for the corresponding validation interceptor), but also optionally specifying
37+
* the validation groups for method-level validation in the annotated class.
38+
* Can also be used as a meta-annotation on a custom stereotype annotation.
39+
*
40+
* @author Juergen Hoeller
41+
* @since 3.1
42+
* @see javax.validation.Validator#validate(Object, Class[])
43+
* @see org.springframework.validation.SmartValidator#validate(Object, org.springframework.validation.Errors, Object...)
44+
* @see org.springframework.validation.beanvalidation.SpringValidatorAdapter
45+
* @see org.springframework.validation.beanvalidation.MethodValidationPostProcessor
46+
*/
47+
@Target({ElementType.TYPE, ElementType.PARAMETER})
48+
@Retention(RetentionPolicy.RUNTIME)
49+
@Documented
50+
public @interface Validated {
51+
52+
/**
53+
* Specify one or more validation groups to apply to the validation step
54+
* kicked off by this annotation.
55+
* <p>JSR-303 defines validation groups as custom annotations which an application declares
56+
* for the sole purpose of using them as type-safe group arguments, as implemented in
57+
* {@link org.springframework.validation.beanvalidation.SpringValidatorAdapter}.
58+
* <p>Other {@link org.springframework.validation.SmartValidator} implementations may
59+
* support class arguments in other ways as well.
60+
*/
61+
Class[] value() default {};
62+
63+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
/*
2+
* Copyright 2002-2011 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.validation.beanvalidation;
18+
19+
import java.util.Set;
20+
import javax.validation.Validation;
21+
import javax.validation.Validator;
22+
import javax.validation.ValidatorFactory;
23+
24+
import org.aopalliance.intercept.MethodInterceptor;
25+
import org.aopalliance.intercept.MethodInvocation;
26+
import org.hibernate.validator.HibernateValidator;
27+
import org.hibernate.validator.method.MethodConstraintViolation;
28+
import org.hibernate.validator.method.MethodConstraintViolationException;
29+
import org.hibernate.validator.method.MethodValidator;
30+
31+
import org.springframework.core.annotation.AnnotationUtils;
32+
import org.springframework.validation.annotation.Validated;
33+
34+
/**
35+
* An AOP Alliance {@link MethodInterceptor} implementation that delegates to a
36+
* JSR-303 provider for performing method-level validation on annotated methods.
37+
*
38+
* <p>Applicable methods have JSR-303 constraint annotations on their parameters
39+
* and/or on their return value (in the latter case specified at the method level,
40+
* typically as inline annotation).
41+
*
42+
* <p>E.g.: <code>public @NotNull Object myValidMethod(@NotNull String arg1, @Max(10) int arg2)</code>
43+
*
44+
* <p>Validation groups can be specified through Spring's {@link Validated} annotation
45+
* at the type level of the containing target class, applying to all public service methods
46+
* of that class. By default, JSR-303 will validate against its default group only.
47+
*
48+
* <p>As of Spring 3.1, this functionality requires Hibernate Validator 4.2 or higher.
49+
* In Spring 3.2, this class will autodetect a Bean Validation 1.1 compliant provider
50+
* and automatically use the standard method validation support there (once available).
51+
*
52+
* @author Juergen Hoeller
53+
* @since 3.1
54+
* @see MethodValidationPostProcessor
55+
* @see org.hibernate.validator.method.MethodValidator
56+
*/
57+
public class MethodValidationInterceptor implements MethodInterceptor {
58+
59+
private final MethodValidator validator;
60+
61+
62+
/**
63+
* Create a new MethodValidationInterceptor using a default JSR-303 validator underneath.
64+
*/
65+
public MethodValidationInterceptor() {
66+
this(Validation.byProvider(HibernateValidator.class).configure().buildValidatorFactory());
67+
}
68+
69+
/**
70+
* Create a new MethodValidationInterceptor using the given JSR-303 ValidatorFactory.
71+
* @param validatorFactory the JSR-303 ValidatorFactory to use
72+
*/
73+
public MethodValidationInterceptor(ValidatorFactory validatorFactory) {
74+
this(validatorFactory.getValidator());
75+
}
76+
77+
/**
78+
* Create a new MethodValidationInterceptor using the given JSR-303 Validator.
79+
* @param validatorFactory the JSR-303 Validator to use
80+
*/
81+
public MethodValidationInterceptor(Validator validator) {
82+
this.validator = validator.unwrap(MethodValidator.class);
83+
}
84+
85+
86+
public Object invoke(MethodInvocation invocation) throws Throwable {
87+
Class[] groups = determineValidationGroups(invocation);
88+
Set<MethodConstraintViolation<Object>> result = this.validator.validateAllParameters(
89+
invocation.getThis(), invocation.getMethod(), invocation.getArguments(), groups);
90+
if (!result.isEmpty()) {
91+
throw new MethodConstraintViolationException(result);
92+
}
93+
Object returnValue = invocation.proceed();
94+
result = this.validator.validateReturnValue(
95+
invocation.getThis(), invocation.getMethod(), returnValue, groups);
96+
if (!result.isEmpty()) {
97+
throw new MethodConstraintViolationException(result);
98+
}
99+
return returnValue;
100+
}
101+
102+
/**
103+
* Determine the validation groups to validate against for the given method invocation.
104+
* <p>Default are the validation groups as specified in the {@link Validated} annotation
105+
* on the containing target class of the method.
106+
* @param invocation the current MethodInvocation
107+
* @return the applicable validation groups as a Class array
108+
*/
109+
protected Class[] determineValidationGroups(MethodInvocation invocation) {
110+
Validated valid = AnnotationUtils.findAnnotation(invocation.getThis().getClass(), Validated.class);
111+
return (valid != null ? valid.value() : new Class[0]);
112+
}
113+
114+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
/*
2+
* Copyright 2002-2011 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.validation.beanvalidation;
18+
19+
import java.lang.annotation.Annotation;
20+
import javax.validation.Validator;
21+
import javax.validation.ValidatorFactory;
22+
23+
import org.aopalliance.aop.Advice;
24+
25+
import org.springframework.aop.Advisor;
26+
import org.springframework.aop.Pointcut;
27+
import org.springframework.aop.framework.Advised;
28+
import org.springframework.aop.framework.AopInfrastructureBean;
29+
import org.springframework.aop.framework.ProxyConfig;
30+
import org.springframework.aop.framework.ProxyFactory;
31+
import org.springframework.aop.support.AopUtils;
32+
import org.springframework.aop.support.DefaultPointcutAdvisor;
33+
import org.springframework.aop.support.annotation.AnnotationMatchingPointcut;
34+
import org.springframework.beans.BeansException;
35+
import org.springframework.beans.factory.BeanClassLoaderAware;
36+
import org.springframework.beans.factory.InitializingBean;
37+
import org.springframework.beans.factory.config.BeanPostProcessor;
38+
import org.springframework.core.Ordered;
39+
import org.springframework.util.Assert;
40+
import org.springframework.util.ClassUtils;
41+
import org.springframework.validation.annotation.Validated;
42+
43+
/**
44+
* A convenient {@link BeanPostProcessor} implementation that delegates to a
45+
* JSR-303 provider for performing method-level validation on annotated methods.
46+
*
47+
* <p>Applicable methods have JSR-303 constraint annotations on their parameters
48+
* and/or on their return value (in the latter case specified at the method level,
49+
* typically as inline annotation).
50+
*
51+
* <p>E.g.: <code>public @NotNull Object myValidMethod(@NotNull String arg1, @Max(10) int arg2)</code>
52+
*
53+
* <p>Target classes with such annotated methods need to be annotated with Spring's
54+
* {@link Validated} annotation at the type level, for their methods to be searched for
55+
* inline constraint annotations. Validation groups can be specified through {@link Validated}
56+
* as well. By default, JSR-303 will validate against its default group only.
57+
*
58+
* <p>As of Spring 3.1, this functionality requires Hibernate Validator 4.2 or higher.
59+
* In Spring 3.2, this class will autodetect a Bean Validation 1.1 compliant provider
60+
* and automatically use the standard method validation support there (once available).
61+
*
62+
* @author Juergen Hoeller
63+
* @since 3.1
64+
* @see MethodValidationInterceptor
65+
* @see org.hibernate.validator.method.MethodValidator
66+
*/
67+
public class MethodValidationPostProcessor extends ProxyConfig
68+
implements BeanPostProcessor, BeanClassLoaderAware, Ordered, InitializingBean {
69+
70+
private Class<? extends Annotation> validatedAnnotationType = Validated.class;
71+
72+
private Validator validator;
73+
74+
private ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader();
75+
76+
private Advisor advisor;
77+
78+
79+
/**
80+
* Set the 'validated' annotation type.
81+
* The default validated annotation type is the {@link Validated} annotation.
82+
* <p>This setter property exists so that developers can provide their own
83+
* (non-Spring-specific) annotation type to indicate that a class is supposed
84+
* to be validated in the sense of applying method validation.
85+
* @param validatedAnnotationType the desired annotation type
86+
*/
87+
public void setValidatedAnnotationType(Class<? extends Annotation> validatedAnnotationType) {
88+
Assert.notNull(validatedAnnotationType, "'validatedAnnotationType' must not be null");
89+
this.validatedAnnotationType = validatedAnnotationType;
90+
}
91+
92+
/**
93+
* Set the JSR-303 Validator to delegate to for validating methods.
94+
* <p>Default is the default ValidatorFactory's default Validator.
95+
*/
96+
public void setValidator(Validator validator) {
97+
this.validator = validator;
98+
}
99+
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+
public void setBeanClassLoader(ClassLoader classLoader) {
111+
this.beanClassLoader = classLoader;
112+
}
113+
114+
public int getOrder() {
115+
// This should run after all other post-processors, so that it can just add
116+
// an advisor to existing proxies rather than double-proxy.
117+
return LOWEST_PRECEDENCE;
118+
}
119+
120+
121+
public void afterPropertiesSet() {
122+
Pointcut pointcut = new AnnotationMatchingPointcut(this.validatedAnnotationType, true);
123+
Advice advice = (this.validator != null ? new MethodValidationInterceptor(this.validator) :
124+
new MethodValidationInterceptor());
125+
this.advisor = new DefaultPointcutAdvisor(pointcut, advice);
126+
}
127+
128+
129+
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
130+
return bean;
131+
}
132+
133+
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
134+
if (bean instanceof AopInfrastructureBean) {
135+
// Ignore AOP infrastructure such as scoped proxies.
136+
return bean;
137+
}
138+
Class<?> targetClass = AopUtils.getTargetClass(bean);
139+
if (AopUtils.canApply(this.advisor, targetClass)) {
140+
if (bean instanceof Advised) {
141+
((Advised) bean).addAdvisor(this.advisor);
142+
return bean;
143+
}
144+
else {
145+
ProxyFactory proxyFactory = new ProxyFactory(bean);
146+
// Copy our properties (proxyTargetClass etc) inherited from ProxyConfig.
147+
proxyFactory.copyFrom(this);
148+
proxyFactory.addAdvisor(this.advisor);
149+
return proxyFactory.getProxy(this.beanClassLoader);
150+
}
151+
}
152+
else {
153+
// This is not a repository.
154+
return bean;
155+
}
156+
}
157+
158+
}

0 commit comments

Comments
 (0)