Why is @Validated required for validating Spring controller request parameters?

Tags: , , , ,



With the following validation setup in an annotated MVC controller:

@RestController
@RequestMapping("/users")
@Validated  // <-- without this, the @Size annotation in setPassword() has no effect
public class UserController {

    @PutMapping("/{id}/password")
    public void setPassword(@PathVariable long id, @RequestBody @Size(min = 8) String password) {
        /* ... */
    }

    @PutMapping("/{id}/other")
    public void setOther(@PathVariable long id, @RequestBody @Valid MyFormObject form) {
        /* ... */
    }
}

@Validated on the controller is required for the method parameter since it’s not a “complex” object. In comparison, the @Valid annotation on the setOther method works without the @Validated annotation.

Why is @Validated required? Why not enable it by default? Is there a cost to its use?

edit

Note that Difference between @Valid and @Validated in Spring is related (I read it before asking this), but it doesn’t address the why in my question.

Answer

Validation of objects is done by Hibernate Validator using the annotations from Jakarta Bean Validation 2.0. Something needs to trigger hibernate validator to run.

SpringMVC calls the controller methods when it sees a parameter with @Valid it will pass that object to hibernate validator. Hibernate validator will

  1. Examine the class of the object to figure out what validation rules have been put on the class fields
  2. Execute the validation rules against the fields marked up with validation annotations.

So in this case

@PutMapping("/{id}/other")
public void setOther(@PathVariable long id, @RequestBody @Valid MyFormObject form) {
        /* ... */
} 

MyFormObject has annotations on it that the hibernate validator can find to validate the object.

In this case

@PutMapping("/{id}/password")
public void setPassword(@PathVariable long id, @RequestBody @Size(min = 8) String password) {
        /* ... */
}

java.lang.String does not have any annotations defined on it for hibernate validator to discover.

@Valid comes from the Bean validation package javax.validation.Valid while @Validated comes from Spring org.springframework.validation.annotation.Validated

@Validated annotation activates the Spring Validation AOP interceptor and it will examine method parameters to see if they have any validation annotations on them, if they do then Spring will call hibernate validator with each specific annotation for example @Size(min = 8) String password means call hibernate size validator and pass the value of the parameter password in this case hibernate validator does not need to scan java.lang.String to see if it has validation annotations on it. @Validated works on any spring @Component you can use it on @Service classes for example.

There is extra overhead for using @Validated similar to using @Transactional so that is why you have to opt into it. In the case of javax.validation.Valid Spring MVC needs to check the annotations on the controller method parameters so when it sees @Valid it is easy for it to send that object the Hibernate Validator without needing to add an AOP interceptor.



Source: stackoverflow