35

嘿嘿,

我想@Validated(group=Foo.class)在执行如下方法之前使用注释来验证参数:

public void doFoo(Foo @Validated(groups=Foo.class) foo){}

当我将此方法放在我的 Spring 应用程序的 Controller 中@Validated时,会在 Foo 对象无效时执行并抛出错误。但是,如果我在应用程序的 Service 层中的方法中放入相同的内容,则不会执行验证,并且即使 Foo 对象无效,该方法也会运行。

不能@Validated在服务层使用注解吗?还是我必须配置一些额外的东西才能使它工作?

更新:

我已将以下两个 bean 添加到我的 service.xml 中:

<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"/>
<bean class="org.springframework.validation.beanvalidation.MethodValidationPostProcessor"/>

并像这样替换@Validate@Null

public void doFoo(Foo @Null(groups=Foo.class) foo){}

我知道这是一个非常愚蠢的注释,但我想检查如果我现在调用该方法并传递 null 它会抛出一个违规异常。那么为什么它执行@Null注解而不是@Validate注解呢?我知道一个javax.validation来自Spring,另一个来自Spring,但我认为这与它无关?

4

5 回答 5

20

在 Spring MVC 堆栈的眼中,没有服务层这样的东西。它适用于@Controller类处理程序方法的原因是 Spring 使用了一个特殊的HandlerMethodArgumentResolver调用ModelAttributeMethodProcessor,它在解析要在处理程序方法中使用的参数之前执行验证。

正如我们所说的,服务层只是一个普通的 bean,没有从 MVC ( DispatcherServlet) 堆栈中添加任何其他行为。因此,您不能期望 Spring 进行任何验证。您需要自己动手,可能使用 AOP。


有了MethodValidationPostProcessor,看看javadoc

适用的方法在其参数和/或返回值上具有 JSR-303 约束注释(在后一种情况下,在方法级别指定,通常作为内联注释)。

验证组可以通过 Spring 的 Validated 注解在包含的目标类的类型级别指定,应用于该类的所有公共服务方法。默认情况下,JSR-303 将仅针对其默认组进行验证。

注释仅用于指定验证组@Validated,它本身并不强制任何验证。您需要使用其中一种javax.validation注释,例如@Nullor @Valid。请记住,您可以在方法参数上使用任意数量的注释。

于 2013-10-17T12:18:58.047 回答
13

作为方法的 Spring Validation 的旁注:

由于 Spring 在其方法中使用了拦截器,因此验证本身仅在您与 Bean 的方法对话时执行:

通过 Spring 或 JSR-303 Validator 接口与此 bean 的实例对话时,您将与底层 ValidatorFactory 的默认 Validator 对话。这非常方便,因为您不必再​​次调用工厂,假设您几乎总是使用默认的验证器。

这很重要,因为如果您尝试以这种方式为类中的方法调用实现验证,它将不起作用。例如:

@Autowired
WannaValidate service;
//...
service.callMeOutside(new Form);

@Service
public class WannaValidate {

    /* Spring Validation will work fine when executed from outside, as above */
    @Validated
    public void callMeOutside(@Valid Form form) {
         AnotherForm anotherForm = new AnotherForm(form);
         callMeInside(anotherForm);
    }

    /* Spring Validation won't work for AnotherForm if executed from inner method */
    @Validated
    public void callMeInside(@Valid AnotherForm form) {
         // stuff
    }        
}

希望有人觉得这很有帮助。使用 Spring 4.3 进行测试,因此其他版本的情况可能会有所不同。

于 2018-03-16T08:30:20.677 回答
7

@pgiecek您不需要创建新的注释。您可以使用:

@Validated
public class MyClass {

    @Validated({Group1.class})
    public myMethod1(@Valid Foo foo) { ... }

    @Validated({Group2.class})
    public myMethod2(@Valid Foo foo) { ... }

    ...
}
于 2015-11-24T08:04:54.470 回答
6

小心鲁本萨的方法。

这仅在您声明@Valid为唯一注释时才有效。当您将它与其他注释结合使用时,@NotNull除了 之外的所有内容都@Valid将被忽略。

以下将不起作用@NotNull将被忽略:

@Validated
public class MyClass {

    @Validated(Group1.class)
    public void myMethod1(@NotNull @Valid Foo foo) { ... }

    @Validated(Group2.class)
    public void myMethod2(@NotNull @Valid Foo foo) { ... }

}

结合其他注释,您还需要声明javax.validation.groups.DefaultGroup,如下所示:

@Validated
public class MyClass {

    @Validated({ Default.class, Group1.class })
    public void myMethod1(@NotNull @Valid Foo foo) { ... }

    @Validated({ Default.class, Group2.class })
    public void myMethod2(@NotNull @Valid Foo foo) { ... }

}
于 2016-08-03T08:19:16.103 回答
5

如上所述,只能通过@Validated类级别的注释来指定验证组。但是,这不是很方便,因为有时您有一个包含多个方法的类,这些方法具有相同的实体作为参数,但每个方法都需要不同的属性子集来验证。这也是我的情况,您可以在下面找到解决它的几个步骤。

1) 实现自定义注释,除了@Validated在类级别指定的组之外,还可以在方法级别指定验证组。

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ValidatedGroups {

    Class<?>[] value() default {};
}

2) 扩展MethodValidationInterceptor和覆盖determineValidationGroups方法如下。

@Override
protected Class<?>[] determineValidationGroups(MethodInvocation invocation) {
    final Class<?>[] classLevelGroups = super.determineValidationGroups(invocation);

    final ValidatedGroups validatedGroups = AnnotationUtils.findAnnotation(
            invocation.getMethod(), ValidatedGroups.class);

    final Class<?>[] methodLevelGroups = validatedGroups != null ? validatedGroups.value() : new Class<?>[0];
    if (methodLevelGroups.length == 0) {
        return classLevelGroups;
    }

    final int newLength = classLevelGroups.length + methodLevelGroups.length;
    final Class<?>[] mergedGroups = Arrays.copyOf(classLevelGroups, newLength);
    System.arraycopy(methodLevelGroups, 0, mergedGroups, classLevelGroups.length, methodLevelGroups.length);

    return mergedGroups;
}

3)实现自己的MethodValidationPostProcessor(只需复制 Spring 之一)并在方法中afterPropertiesSet使用步骤 2 中实现的验证拦截器。

@Override
public void afterPropertiesSet() throws Exception {
    Pointcut pointcut = new AnnotationMatchingPointcut(Validated.class, true);
    Advice advice = (this.validator != null ? new ValidatedGroupsAwareMethodValidationInterceptor(this.validator) :
            new ValidatedGroupsAwareMethodValidationInterceptor());
    this.advisor = new DefaultPointcutAdvisor(pointcut, advice);
}

4)注册您的验证后处理器,而不是 Spring 之一。

<bean class="my.package.ValidatedGroupsAwareMethodValidationPostProcessor"/> 

而已。现在您可以按如下方式使用它。

@Validated(groups = Group1.class)   
public class MyClass {

    @ValidatedGroups(Group2.class)
    public myMethod1(Foo foo) { ... }

    public myMethod2(Foo foo) { ... }

    ...
}
于 2014-02-09T14:03:58.720 回答