diff --git a/spring-context/src/main/java/org/springframework/validation/annotation/Validated.java b/spring-context/src/main/java/org/springframework/validation/annotation/Validated.java index e1b2b0930d..caf53d56ac 100644 --- a/spring-context/src/main/java/org/springframework/validation/annotation/Validated.java +++ b/spring-context/src/main/java/org/springframework/validation/annotation/Validated.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2011 the original author or authors. + * Copyright 2002-2014 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. @@ -35,7 +35,11 @@ import java.lang.annotation.Target; * class is supposed to be validated at the method level (acting as a pointcut * for the corresponding validation interceptor), but also optionally specifying * the validation groups for method-level validation in the annotated class. - * Can also be used as a meta-annotation on a custom stereotype annotation. + * Applying this annotation at the method level allows for overriding the + * validation groups for a specific method but does not serve as a pointcut; + * a class-level annotation is nevertheless necessary to trigger method validation + * for a specific bean to begin with. Can also be used as a meta-annotation on a + * custom stereotype annotation or a custom group-specific validated annotation. * * @author Juergen Hoeller * @since 3.1 @@ -44,7 +48,7 @@ import java.lang.annotation.Target; * @see org.springframework.validation.beanvalidation.SpringValidatorAdapter * @see org.springframework.validation.beanvalidation.MethodValidationPostProcessor */ -@Target({ElementType.TYPE, ElementType.PARAMETER}) +@Target({ElementType.TYPE, ElementType.METHOD, ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Validated { diff --git a/spring-context/src/main/java/org/springframework/validation/beanvalidation/MethodValidationInterceptor.java b/spring-context/src/main/java/org/springframework/validation/beanvalidation/MethodValidationInterceptor.java index a9ce695e8e..befc4b9f2d 100644 --- a/spring-context/src/main/java/org/springframework/validation/beanvalidation/MethodValidationInterceptor.java +++ b/spring-context/src/main/java/org/springframework/validation/beanvalidation/MethodValidationInterceptor.java @@ -141,8 +141,11 @@ public class MethodValidationInterceptor implements MethodInterceptor { * @return the applicable validation groups as a Class array */ protected Class[] determineValidationGroups(MethodInvocation invocation) { - Validated valid = AnnotationUtils.findAnnotation(invocation.getThis().getClass(), Validated.class); - return (valid != null ? valid.value() : new Class[0]); + Validated validatedAnn = AnnotationUtils.findAnnotation(invocation.getMethod(), Validated.class); + if (validatedAnn == null) { + validatedAnn = AnnotationUtils.findAnnotation(invocation.getThis().getClass(), Validated.class); + } + return (validatedAnn != null ? validatedAnn.value() : new Class[0]); } diff --git a/spring-context/src/test/java/org/springframework/validation/beanvalidation/MethodValidationTests.java b/spring-context/src/test/java/org/springframework/validation/beanvalidation/MethodValidationTests.java index a7c8ffb594..d13a6e99a3 100644 --- a/spring-context/src/test/java/org/springframework/validation/beanvalidation/MethodValidationTests.java +++ b/spring-context/src/test/java/org/springframework/validation/beanvalidation/MethodValidationTests.java @@ -124,7 +124,8 @@ public class MethodValidationTests { @NotNull Object myValidMethod(@NotNull(groups = MyGroup.class) String arg1, @Max(10) int arg2); - @Async void myValidAsyncMethod(@NotNull(groups = MyGroup.class) String arg1, @Max(10) int arg2); + @MyValid + @Async void myValidAsyncMethod(@NotNull(groups = OtherGroup.class) String arg1, @Max(10) int arg2); } @@ -132,9 +133,19 @@ public class MethodValidationTests { } + public interface OtherGroup { + } + + @Validated({MyGroup.class, Default.class}) @Retention(RetentionPolicy.RUNTIME) public @interface MyStereotype { } + + @Validated({OtherGroup.class, Default.class}) + @Retention(RetentionPolicy.RUNTIME) + public @interface MyValid { + } + }