Java Spring @Validated 在服务层
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/19425221/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Spring @Validated in service layer
提问by Tom
Hej,
嘿嘿
I want to use the @Validated(group=Foo.class)
annotation to validate an argument before executing a method like following:
我想@Validated(group=Foo.class)
在执行如下方法之前使用注释来验证参数:
public void doFoo(Foo @Validated(groups=Foo.class) foo){}
When i put this method in the Controller of my Spring application, the @Validated
is executed and throws an error when the Foo object is not valid. However if I put the same thing in a method in the Service layer of my application, the validation is not executed and the method just runs even when the Foo object isn't valid.
当我将此方法放在我的 Spring 应用程序的控制器中@Validated
时,当 Foo 对象无效时,它会被执行并抛出错误。但是,如果我将相同的内容放在应用程序服务层的方法中,则不会执行验证,即使 Foo 对象无效,该方法也会运行。
Can't you use the @Validated
annotation in the service layer ? Or do I have to do configure something extra to make it work ?
@Validated
服务层的注解不能用吗?还是我必须做一些额外的配置才能使其工作?
Update:
更新:
I have added the following two beans to my service.xml:
我已将以下两个 bean 添加到我的 service.xml:
<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"/>
<bean class="org.springframework.validation.beanvalidation.MethodValidationPostProcessor"/>
and replaced the @Validate
with @Null
like so:
并@Validate
用@Null
如下替换:
public void doFoo(Foo @Null(groups=Foo.class) foo){}
I know it is a pretty silly annotation to do but I wanted to check that if I call the method now and passing null it would throw an violation exception which it does. So why does it execute the @Null
annotation and not the @Validate
annotation ? I know one is from javax.validation
and the other is from Spring but I do not think that has anything to do with it ?
我知道这是一个非常愚蠢的注释,但我想检查一下,如果我现在调用该方法并传递 null,它会抛出一个违规异常。那么为什么它执行@Null
注解而不是@Validate
注解呢?我知道一个javax.validation
来自 Spring,另一个来自 Spring,但我认为这与它无关?
回答by Sotirios Delimanolis
In the eyes of a Spring MVC stack, there is no such thing as a service layer. The reason it works for @Controller
class handler methods is that Spring uses a special HandlerMethodArgumentResolver
called ModelAttributeMethodProcessor
which performs validation before resolving the argument to use in your handler method.
在 Spring MVC 堆栈的眼中,没有服务层这样的东西。它适用于@Controller
类处理程序方法的原因是 Spring 使用一个特殊的HandlerMethodArgumentResolver
调用ModelAttributeMethodProcessor
,它在解析要在处理程序方法中使用的参数之前执行验证。
The service layer, as we call it, is just a plain bean with no additional behavior added to it from the MVC (DispatcherServlet
) stack. As such you cannot expect any validation from Spring. You need to roll your own, probably with AOP.
服务层,正如我们所说的,只是一个普通的 bean,没有从 MVC ( DispatcherServlet
) 堆栈中添加额外的行为。因此,您不能期望来自 Spring 的任何验证。您需要自己动手,可能使用 AOP。
With MethodValidationPostProcessor
, take a look at the javadoc
用MethodValidationPostProcessor
,看看javadoc
Applicable methods have JSR-303 constraint annotations on their parameters and/or on their return value (in the latter case specified at the method level, typically as inline annotation).
Validation groups can be specified through Spring's Validated annotation at the type level of the containing target class, applying to all public service methods of that class. By default, JSR-303 will validate against its default group only.
适用的方法在其参数和/或返回值上具有 JSR-303 约束注释(在后一种情况下在方法级别指定,通常作为内联注释)。
验证组可以通过 Spring 的 Validated annotation 在包含目标类的类型级别指定,应用于该类的所有公共服务方法。默认情况下,JSR-303 将仅针对其默认组进行验证。
The @Validated
annotation is only used to specify a validation group, it doesn't itself force any validation. You need to use one of the javax.validation
annotations like @Null
or @Valid
. Remember that you can use as many annotations as you would like on a method parameter.
该@Validated
注释仅用于指定验证组,它本身并不强迫任何验证。您需要使用javax.validation
诸如@Null
或之类的注释之一@Valid
。请记住,您可以在方法参数上使用任意数量的注释。
回答by pgiecek
As stated above to specify validation groups is possible only through @Validated
annotation at class level. However, it is not very convenient since sometimes you have a class containing several methods with the same entity as a parameter but each of which requiring different subset of properties to validate. It was also my case and below you can find several steps to take to solve it.
如上所述,只有通过@Validated
类级别的注释才能指定验证组。但是,这不是很方便,因为有时您有一个包含多个方法的类,这些方法具有相同的实体作为参数,但每个方法都需要不同的属性子集来验证。这也是我的情况,在下面你可以找到几个步骤来解决它。
1) Implement custom annotation that enables to specify validation groups at method level in addition to groups specified through @Validated
at class level.
1) 实现自定义注释,除了@Validated
在类级别指定的组之外,还可以在方法级别指定验证组。
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ValidatedGroups {
Class<?>[] value() default {};
}
2) Extend MethodValidationInterceptor
and override determineValidationGroups
method as follows.
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) Implement your own MethodValidationPostProcessor
(just copy the Spring one) and in the method afterPropertiesSet
use validation interceptor implemented in step 2.
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) Register your validation post processor instead of Spring one.
4) 注册您的验证后处理器而不是 Spring 一个。
<bean class="my.package.ValidatedGroupsAwareMethodValidationPostProcessor"/>
That's it. Now you can use it as follows.
就是这样。现在您可以按如下方式使用它。
@Validated(groups = Group1.class)
public class MyClass {
@ValidatedGroups(Group2.class)
public myMethod1(Foo foo) { ... }
public myMethod2(Foo foo) { ... }
...
}
回答by rubensa
@pgiecek You don't need to create a new Annotation. You can use:
@pgiecek 您不需要创建新的注释。您可以使用:
@Validated
public class MyClass {
@Validated({Group1.class})
public myMethod1(@Valid Foo foo) { ... }
@Validated({Group2.class})
public myMethod2(@Valid Foo foo) { ... }
...
}
回答by thunderhook
Be careful with rubensa's approach.
小心 rubensa 的方法。
This only works when you declare @Valid
as the only annotation. When you combine it with other annotations like @NotNull
everything except the @Valid
will be ignored.
这仅在您声明@Valid
为唯一注释时才有效。当您将它与其他注释(例如@NotNull
除 the 之外的所有内容)结合使用时,@Valid
将被忽略。
The following will not workand the @NotNull
will be ignored:
在下面将无法正常工作和@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) { ... }
}
In combination with other annotations you need to declare the javax.validation.groups.Default
Group as well, like this:
结合其他注释,您还需要声明javax.validation.groups.Default
Group,如下所示:
@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) { ... }
}
回答by Vladimir Salin
As a side note on Spring Validation for methods:
作为 Spring Validation 方法的旁注:
Since Spring uses interceptors in its approach, the validation itself is only performed when you're talking to a Bean's method:
由于 Spring 在其方法中使用拦截器,因此验证本身仅在您与 Bean 的方法交谈时执行:
When talking to an instance of this bean through the Spring or JSR-303 Validator interfaces, you'll be talking to the default Validator of the underlying ValidatorFactory. This is very convenient in that you don't have to perform yet another call on the factory, assuming that you will almost always use the default Validator anyway.
当通过 Spring 或 JSR-303 Validator 接口与这个 bean 的实例交谈时,您将与底层 ValidatorFactory 的默认 Validator 交谈。这非常方便,因为您不必再对工厂执行一次调用,假设您几乎总是使用默认的 Validator。
This is important because if you're trying to implement a validation in such a way for method calls within the class, it won't work. E.g.:
这很重要,因为如果您试图以这种方式为类中的方法调用实现验证,它将不起作用。例如:
@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
}
}
Hope someone finds this helpful. Tested with Spring 4.3, so things might be different for other versions.
希望有人觉得这有帮助。用 Spring 4.3 测试过,所以其他版本的情况可能会有所不同。