Tom*_*Tom 20 java validation spring spring-mvc
HEJ,
我想@Validated(group=Foo.class)在执行如下方法之前使用注释来验证参数:
public void doFoo(Foo @Validated(groups=Foo.class) foo){}
Run Code Online (Sandbox Code Playgroud)
当我将此方法放在我的Spring应用程序的Controller中时,@Validated执行并在Foo对象无效时抛出错误.但是,如果我在应用程序的Service层中的方法中放置相同的东西,则不执行验证,即使Foo对象无效,该方法也会运行.
你不能@Validated在服务层使用注释吗?或者我是否必须配置额外的东西以使其工作?
更新:
我在service.xml中添加了以下两个bean:
<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"/>
<bean class="org.springframework.validation.beanvalidation.MethodValidationPostProcessor"/>
Run Code Online (Sandbox Code Playgroud)
并取代了@Validate用@Null像这样:
public void doFoo(Foo @Null(groups=Foo.class) foo){}
Run Code Online (Sandbox Code Playgroud)
我知道这是一个非常愚蠢的注释,但我想检查一下,如果我现在调用该方法并传递null,它将抛出违规异常.那为什么它执行@Null注释而不是@Validate注释?我知道一个javax.validation来自Spring,另一个来自Spring,但我不认为这与它有什么关系?
Sot*_*lis 13
在Spring MVC堆栈的眼中,没有服务层这样的东西.它适用于@Controller类处理程序方法的原因是Spring使用一个特殊的HandlerMethodArgumentResolver调用ModelAttributeMethodProcessor,它在解析要在处理程序方法中使用的参数之前执行验证.
我们称之为服务层,它只是一个普通的bean,没有从MVC(DispatcherServlet)堆栈添加任何额外的行为.因此,您不能指望Spring的任何验证.你需要自己动手,可能是AOP.
有了MethodValidationPostProcessor,看看javadoc
适用的方法对其参数和/或其返回值具有JSR-303约束注释(在后一种情况下,在方法级别指定,通常作为内联注释).
验证组可以通过Spring的Validated注释在包含目标类的类型级别指定,适用于该类的所有公共服务方法.默认情况下,JSR-303将仅针对其默认组进行验证.
该@Validated注释仅用于指定验证组,它本身并不强迫任何验证.您需要使用其中一个javax.validation注释,如@Null或@Valid.请记住,您可以在方法参数上使用任意数量的注释.
Vla*_*lin 12
作为 Spring Validation 方法的旁注:
由于 Spring 在其方法中使用拦截器,因此验证本身仅在您与 Bean 的方法交谈时执行:
当通过 Spring 或 JSR-303 Validator 接口与这个 bean 的实例交谈时,您将与底层 ValidatorFactory 的默认 Validator 交谈。这非常方便,因为您不必再对工厂执行另一个调用,假设您几乎总是使用默认的 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
}
}
Run Code Online (Sandbox Code Playgroud)
希望有人觉得这有帮助。用 Spring 4.3 测试过,所以其他版本的情况可能会有所不同。
小心 rubensa 的方法。
这仅在您声明@Valid为唯一注释时才有效。当您将其与其他注释(例如@NotNull除 the 之外的所有注释)结合使用时,@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) { ... }
}
Run Code Online (Sandbox Code Playgroud)
结合其他注释,您还需要声明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) { ... }
}
Run Code Online (Sandbox Code Playgroud)
如上所述,只有通过@Validated类级别的注释才能指定验证组。但是,这不是很方便,因为有时您有一个包含多个方法的类,这些方法具有相同的实体作为参数,但每个方法都需要不同的属性子集来验证。这也是我的情况,在下面你可以找到几个步骤来解决它。
1) 实现自定义注释,除了@Validated在类级别指定的组之外,还可以在方法级别指定验证组。
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ValidatedGroups {
Class<?>[] value() default {};
}
Run Code Online (Sandbox Code Playgroud)
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;
}
Run Code Online (Sandbox Code Playgroud)
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);
}
Run Code Online (Sandbox Code Playgroud)
4) 注册您的验证后处理器而不是 Spring 一个。
<bean class="my.package.ValidatedGroupsAwareMethodValidationPostProcessor"/>
Run Code Online (Sandbox Code Playgroud)
就是这样。现在您可以按如下方式使用它。
@Validated(groups = Group1.class)
public class MyClass {
@ValidatedGroups(Group2.class)
public myMethod1(Foo foo) { ... }
public myMethod2(Foo foo) { ... }
...
}
Run Code Online (Sandbox Code Playgroud)
@pgiecek您无需创建新的注释.您可以使用:
@Validated
public class MyClass {
@Validated({Group1.class})
public myMethod1(@Valid Foo foo) { ... }
@Validated({Group2.class})
public myMethod2(@Valid Foo foo) { ... }
...
}
Run Code Online (Sandbox Code Playgroud)