Backend Development 16 min read

Comprehensive Guide to Spring Validation: Annotations, Usage Scenarios, and Advanced Techniques

This article provides an in‑depth overview of Spring Validation, covering standard Bean Validation 2.0 annotations, Hibernate extensions, the differences between @Valid and @Validated, practical usage in controllers, programmatic validation, Dubbo integration, group validation, and how to create custom validation annotations with full code examples.

政采云技术
政采云技术
政采云技术
Comprehensive Guide to Spring Validation: Annotations, Usage Scenarios, and Advanced Techniques

Background

Spring Framework, widely used in Java enterprise development, includes a practical field validation mechanism called Spring Validation that integrates the JSR‑380 (Bean Validation 2.0) specification.

Common Annotations

Bean Validation 2.0 Annotations

Null Checks

@Null : validates that the object is null

@NotNull : validates that the object is not null

@NotEmpty : validates that the object is not null and its size (array, collection, string, etc.) is greater than 0

@NotBlank : validates that a string is not null and, after trimming, has length greater than 0

Size Checks

@Size(min=, max=) : validates that the length of an array, collection, string, etc., falls within the given range

@Min(value) : validates that a numeric value is greater than or equal to the specified minimum

@Max(value) : validates that a numeric value is less than or equal to the specified maximum

Boolean Checks

@AssertTrue : validates that a Boolean is true

@AssertFalse : validates that a Boolean is false

Date & Time Checks

@Past : validates that a Date/Calendar is before the current moment

@Future : validates that a Date/Calendar is after the current moment

@PastOrPresent : validates that a date is in the past or present

@FutureOrPresent : validates that a date is in the present or future

Regular Expression

@Pattern(regexp=, flags=) : validates that a string matches the given regular expression

Hibernate Validation Extensions

@Length(min=, max=) : validates that a string's length is within the specified range

@Range(min=, max=) : validates that a numeric value falls within the given range

@UniqueElements : validates that all elements in a collection are unique (relies on equals )

@ScriptAssert : validates using a custom script

@Valid and @Validated

Both annotations trigger validation, but they differ in usage scope and supported attributes. @Validated can specify validation groups, while @Valid cannot.

// Used on classes, interfaces, enums, methods, and parameters
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Validated {
    // Validation groups
    Class
[] value() default {};
}
// Used on methods, fields, constructors, parameters, and type uses
@Target({METHOD, FIELD, CONSTRUCTOR, PARAMETER, TYPE_USE})
@Retention(RUNTIME)
@Documented
public @interface Valid {
    // No additional attributes
}

Scope difference : @Validated cannot be placed on fields, while @Valid cannot be placed on classes.

Attribute difference : @Validated supports group specification; @Valid does not.

Field Validation Scenarios and Usage Examples

Common scenarios include controller‑level validation, programmatic validation, and Dubbo interface validation.

Controller‑Level Validation

Usage

When a method parameter is annotated with @RequestBody , place @Validated or @Valid before the parameter to enable validation.

@PostMapping("/save")
public Response
saveNotice(@Validated @RequestBody NoticeDTO noticeDTO) {
    // noticeDTO fields are validated before business logic executes
    return Response.ok(true);
}

When using simple parameters annotated with @PathVariable or @RequestParam , add @Validated at the controller class level to activate validation.

@RequestMapping("/notice")
@RestController
@Validated
public class UserController {
    @GetMapping("{id}")
    public Response
detail(@PathVariable("id") @Min(1L) Long noticeId) {
        // noticeId validated, then business logic runs
        return Response.ok();
    }

    @GetMapping("getByTitle")
    public Result getByTitle(@RequestParam("title") @Length(min = 1, max = 20) String title) {
        // title validated, then business logic runs
        return Result.ok();
    }
}

Underlying Mechanism

Spring uses HandlerMethodArgumentResolver to resolve method arguments and invoke validation when appropriate.

The implementation for @RequestBody (e.g., RequestResponseBodyMethodProcessor ) calls validateIfApplicable to perform validation.

validateIfApplicable checks for @Validated or any annotation starting with "Valid" and triggers the validator.

For @PathVariable and @RequestParam , the resolver does not contain built‑in validation logic, so the controller must be annotated with @Validated to enable it.

public interface HandlerMethodArgumentResolver {
    // Determine if the resolver supports the given method parameter
    boolean supportsParameter(MethodParameter parameter);

    // Resolve the argument value
    Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
                         NativeWebRequest request, @Nullable WebDataBinderFactory binderFactory) throws Exception;
}

Programmatic Validation

Configure a Validator bean, optionally enabling fast‑fail mode.

@Configuration
public class ValidatorConfiguration {
    @Bean
    public Validator validator() {
        ValidatorFactory validatorFactory = Validation.byProvider(HibernateValidator.class)
            .configure()
            // .failFast(true) // enable fast‑fail if needed
            .buildValidatorFactory();
        return validatorFactory.getValidator();
    }
}

Inject the validator and validate objects manually.

public class TestValidator {
    @Resource
    private javax.validation.Validator validator;

    public String testMethod(TestRequest request) {
        Set
> violations = validator.validate(request);
        // Assemble violation messages and return
        return res;
    }
}

Dubbo Interface Validation

Enable validation on the provider side by setting validation="true" in the @DubboService annotation.

@DubboService(version = "1.0.0", validation = "true")
public class DubboApiImpl implements DubboApi {
    // ...
}

Customize the error response by implementing a Dubbo Filter that performs validation and formats the result.

@Activate(group = {"provider"}, value = {"customValidationFilter"}, order = 10000)
@Slf4j
public class CustomValidationFilter implements Filter {
    private javax.validation.Validator validator;

    // Dubbo will call this setter to inject the validator
    public void setValidator(javax.validation.Validator validator) {
        this.validator = validator;
    }

    public Result invoke(Invoker
invoker, Invocation invocation) throws RpcException {
        if (this.validator != null && !invocation.getMethodName().startsWith("$")) {
            // Perform field validation, assemble error info, handle exceptions
        }
        return invoker.invoke(invocation);
    }
}

Advanced Usage

Group Validation

Different validation rules can be applied to the same DTO in different scenarios by specifying groups in the annotation and using @Validated(Group.class) on the method.

@Data
public class NoticeDTO {
    @Min(value = 0L, groups = Update.class)
    private Long id;

    @NotNull(groups = {Save.class, Update.class})
    @Length(min = 2, max = 10, groups = {Save.class, Update.class})
    private String title;

    public interface Save {}
    public interface Update {}
}
@PostMapping("/save")
public Response
saveNotice(@RequestBody @Validated(NoticeDTO.Save.class) NoticeDTO noticeDTO) {
    return Response.ok();
}

@PostMapping("/update")
public Response
updateNotice(@RequestBody @Validated(NoticeDTO.Update.class) NoticeDTO noticeDTO) {
    return Response.ok();
}

Custom Validation Annotation

To implement bespoke validation logic, define a custom annotation and its corresponding ConstraintValidator .

@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Constraint(validatedBy = UniqueValidator.class)
public @interface Unique {
    String message() default "Duplicate field values found";
    String[] field();
    Class
[] groups() default {};
}
public class UniqueValidator implements ConstraintValidator
> {
    private Unique unique;

    @Override
    public void initialize(Unique constraintAnnotation) {
        this.unique = constraintAnnotation;
    }

    @Override
    public boolean isValid(Collection
collection, ConstraintValidatorContext context) {
        if (collection == null || collection.isEmpty()) {
            return true;
        }
        return Arrays.stream(unique.field())
            .filter(f -> f != null && !"".equals(f.trim()))
            .allMatch(fieldName -> {
                int distinctCount = (int) collection.stream()
                    .filter(Objects::nonNull)
                    .map(item -> {
                        try {
                            Field field = item.getClass().getField(fieldName);
                            field.setAccessible(true);
                            return field.get(item);
                        } catch (Exception e) {
                            return null;
                        }
                    })
                    .collect(Collectors.collectingAndThen(Collectors.toSet(), Set::size));
                return distinctCount == collection.size();
            });
    }
}

Conclusion

This article demonstrates the inner workings of Spring Validation and shows how to apply standard annotations, extend them with Hibernate, perform validation in controllers, programmatically, and via Dubbo, as well as how to leverage group validation and create custom constraints. Mastering these techniques is essential for Java developers seeking high‑quality, reliable code.

References

JSR‑380 Specification: Java Community Process

Bean Validation website: beanvalidation.org

Hibernate Validator on GitHub

BackendDubboSpringBean ValidationValidationAnnotation
政采云技术
Written by

政采云技术

ZCY Technology Team (Zero), based in Hangzhou, is a growth-oriented team passionate about technology and craftsmanship. With around 500 members, we are building comprehensive engineering, project management, and talent development systems. We are committed to innovation and creating a cloud service ecosystem for government and enterprise procurement. We look forward to your joining us.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.