Spring @Validated在服务层
HEJ,
我想在执行如下方法之前使用@Validated(group=Foo.class)
注释来validation参数:
public void doFoo(Foo @Validated(groups=Foo.class) foo){}
当我将此方法放在我的Spring应用程序的Controller中时,执行@Validated
并在Foo对象无效时抛出错误。 但是,如果我在我的应用程序的Service层中的方法中放置相同的东西,则不会执行validation,即使Foo对象无效,该方法也会运行。
你不能在服务层使用@Validated
注释吗? 或者我是否必须配置额外的东西以使其工作?
更新:
我在service.xml中添加了以下两个bean:
并用@Validate
替换@Validate
, @Null
所示:
public void doFoo(Foo @Null(groups=Foo.class) foo){}
我知道这是一个非常愚蠢的注释,但我想检查一下,如果我现在调用该方法并传递null,它将抛出违规exception。 那么为什么它执行@Null
注释而不是@Validate
注释呢? 我知道一个来自javax.validation
而另一个来自Spring,但我不认为这与它有什么关系?
在Spring MVC堆栈的眼中,没有服务层这样的东西。 它适用于@Controller
类处理程序方法的原因是Spring使用一个名为ModelAttributeMethodProcessor
的特殊HandlerMethodArgumentResolver
,它在解析要在处理程序方法中使用的参数之前执行validation。
我们称之为服务层,它只是一个普通的bean,没有从MVC( DispatcherServlet
)堆栈添加任何额外的行为。 因此,您不能指望Spring的任何validation。 你需要自己动手,可能还有AOP。
使用MethodValidationPostProcessor
,查看javadoc
适用的方法对其参数和/或其返回值具有JSR-303约束注释(在后一种情况下,在方法级别指定,通常作为内联注释)。
validation组可以通过Spring的Validated注释在包含目标类的类型级别指定,适用于该类的所有公共服务方法。 默认情况下,JSR-303将仅针对其默认组进行validation。
@Validated
注释仅用于指定validation组,它本身不会强制进行任何validation。 您需要使用其中一个javax.validation
注释,如@Null
或@Valid
。 请记住,您可以在方法参数上使用任意数量的注释。
@pgiecek您无需创建新的注释。 您可以使用:
@Validated public class MyClass { @Validated({Group1.class}) public myMethod1(@Valid Foo foo) { ... } @Validated({Group2.class}) public myMethod2(@Valid Foo foo) { ... } ... }
如上所述,只能通过类级别的@Validated
注释来指定validation组。 但是,它不是很方便,因为有时你有一个类包含几个方法,这些方法与参数具有相同的实体,但每个方法都需要不同的属性子集来validation。 这也是我的情况,下面你可以找到几个步骤来解决它。
1)实现自定义注释,除了在类级别通过@Validated
指定的组之外,还可以在方法级别指定validation组。
@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中实现的validation拦截器。
@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)注册validation后处理器而不是Spring。
而已。 现在您可以按如下方式使用它。
@Validated(groups = Group1.class) public class MyClass { @ValidatedGroups(Group2.class) public myMethod1(Foo foo) { ... } public myMethod2(Foo foo) { ... } ... }
小心rubensa的方法。
这仅在将@Valid
声明为唯一注释时才有效。
以下方法无效 ,@ @NotNull
将被忽略:
@Validated public class MyClass { @Validated(Group1.class) public myMethod1(@NotNull @Valid Foo foo) { ... } @Validated(Group2.class) public myMethod2(@NotNull @Valid Foo foo) { ... } }
结合其他注释,您还必须声明javax.validation.groups.Default
组,如下所示:
@Validated public class MyClass { @Validated({ Default.class, Group1.class }) public myMethod1(@NotNull @Valid Foo foo) { ... } @Validated({ Default.class, Group2.class }) public myMethod2(@NotNull @Valid Foo foo) { ... } }
作为方法的Spring Validation的附注:
由于Spring使用拦截器方法,因此validation本身仅在您与Bean的方法交谈时执行:
通过Spring或JSR-303 Validator接口与该bean的实例进行通信时,您将与底层ValidatorFactory的默认Validator进行通信。 这非常方便,因为您不必在工厂执行另一个调用,假设您几乎总是会使用默认的Validator。
这很重要,因为如果您尝试以类似方式实现类中的方法调用validation,那么它将无法工作。 例如:
@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进行测试,因此其他版本可能会有所不同。