validator validations custom beans java spring validation spring-mvc

java - validations - Spring @Validated en capa de servicio



spring validation required (5)

Hej

Quiero usar la @Validated(group=Foo.class) para validar un argumento antes de ejecutar un método como el siguiente:

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

Cuando coloco este método en el Controlador de mi aplicación Spring, @Validated se ejecuta y @Validated un error cuando el objeto Foo no es válido. Sin embargo, si pongo lo mismo en un método en la capa de servicio de mi aplicación, la validación no se ejecuta y el método simplemente se ejecuta incluso cuando el objeto Foo no es válido.

¿No puede utilizar la anotación @Validated en la capa de servicio? ¿O tengo que configurar algo extra para que funcione?

Actualizar:

He agregado los siguientes dos beans a mi service.xml:

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

y reemplazó el @Validate con @Null así:

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

Sé que es una anotación bastante tonta, pero quería comprobar que si llamo al método ahora y pasa nulo, se generará una excepción de infracción, lo que sí ocurre. Entonces, ¿por qué ejecuta la anotación @Null y no la anotación @Validate ? Sé que una es de javax.validation y la otra es de Spring, pero no creo que tenga nada que ver con eso.


@pgiecek No es necesario crear una nueva anotación. Puedes usar:

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


A los ojos de una pila Spring MVC, no existe una capa de servicio. La razón por la que funciona para los métodos del controlador de clase @Controller es que Spring usa un HandlerMethodArgumentResolver especial llamado ModelAttributeMethodProcessor que realiza la validación antes de resolver el argumento para usar en su método de controlador.

La capa de servicio, como la llamamos, es solo un bean sin agregación de comportamiento adicional de la pila MVC ( DispatcherServlet ). Como tal, no puede esperar ninguna validación de Spring. Necesitas rodar el tuyo, probablemente con AOP.

Con MethodValidationPostProcessor , eche un vistazo al javadoc

Los métodos aplicables tienen anotaciones de restricción JSR-303 en sus parámetros y / o en su valor de retorno (en el último caso especificado en el nivel de método, generalmente como anotación en línea).

Los grupos de validación se pueden especificar a través de la anotación Validada de Spring en el nivel de tipo de la clase objetivo que contiene, aplicándose a todos los métodos de servicio público de esa clase. De forma predeterminada, JSR-303 se validará solo en su grupo predeterminado.

La anotación @Validated solo se usa para especificar un grupo de validación, no fuerza por sí misma ninguna validación. javax.validation usar una de las anotaciones de @Null como @Null o @Valid . Recuerde que puede usar tantas anotaciones como desee en un parámetro de método.


Como nota al margen sobre la validación de primavera para los métodos

Dado que Spring utiliza interceptores en su enfoque, la validación en sí misma solo se realiza cuando se está hablando con un método de Bean:

Cuando hable con una instancia de este bean a través de las interfaces Spring o JSR-303 Validator, hablará con el Validator predeterminado de la ValidatorFactory subyacente. Esto es muy conveniente ya que no tiene que realizar otra llamada en la fábrica, suponiendo que casi siempre usará el Validador predeterminado de todos modos.

Esto es importante porque si intenta implementar una validación de tal manera para las llamadas de método dentro de la clase, no funcionará. P.ej:

@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 } }

Espero que alguien encuentre esto útil. Probado con Spring 4.3, por lo que las cosas podrían ser diferentes para otras versiones.


Como se indicó anteriormente, para especificar grupos de validación es posible solo a través de la anotación @Validated a nivel de clase. Sin embargo, no es muy conveniente ya que a veces tiene una clase que contiene varios métodos con la misma entidad como parámetro, pero cada uno de los cuales requiere validar un subconjunto diferente de propiedades. También fue mi caso y a continuación puede encontrar varios pasos a seguir para resolverlo.

1) Implementar una anotación personalizada que permita especificar grupos de validación a nivel de método además de los grupos especificados a través de @Validated a nivel de clase.

@Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface ValidatedGroups { Class<?>[] value() default {}; }

2) Extienda MethodValidationInterceptor y anule el método determineValidationGroups siguiente manera.

@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) Implemente su propio MethodValidationPostProcessor (solo copie el Spring one) y en el método afterPropertiesSet utilice el interceptor de validación implementado en el paso 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) Registre su post procesador de validación en lugar de Spring One.

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

Eso es. Ahora puedes usarlo de la siguiente manera.

@Validated(groups = Group1.class) public class MyClass { @ValidatedGroups(Group2.class) public myMethod1(Foo foo) { ... } public myMethod2(Foo foo) { ... } ... }


Ten cuidado con el enfoque de rubensa.

Esto solo funciona cuando declara @Valid como la única anotación. Cuando lo combine con otras anotaciones como @NotNull todo, excepto el @Valid , será ignorado.

Lo siguiente no funcionará y se ignorará @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) { ... } }

En combinación con otras anotaciones, debe declarar también el Grupo javax.validation.groups.Default , como este:

@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) { ... } }