Mastering Spring Boot Parameter Validation with JSR‑303, Groups, and AOP
Learn how to implement comprehensive parameter validation in Spring Boot 2.6.12 using JSR‑303 Bean Validation, covering simple checks, validation groups, single‑parameter validation, nested objects, custom utilities, internationalization, and unified AOP‑based handling with practical code examples and configuration steps.
Environment: SpringBoot 2.6.12.
In real development most APIs need parameter validation, which can involve simple data types or complex objects. This article demonstrates seven validation techniques.
Simple parameter validation
Validation groups
Single parameter validation
Nested parameter validation
Custom utility class validation
Internationalization support
AOP unified parameter handling
What is JSR?
JSR stands for Java Specification Requests, a formal request to add a standardized API to the Java platform. JSR‑303 is the Bean Validation specification defined in Java EE 6. Hibernate Validator is the reference implementation providing built‑in constraints and additional ones.
1. Add Dependencies
<code><dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies></code>The AspectJ dependencies are required for the AOP‑based unified validation.
2. Simple Parameter Validation
<code>public class Users {
@NotEmpty(message = "姓名必需填写")
private String name;
@Min(value = 10, message = "年龄不能小于 10")
private Integer age;
@Length(min = 6, max = 18, message = "邮箱介于 6 到 18 之间")
private String email;
@NotEmpty(message = "电话必需填写")
private String phone;
// interfaces used in later examples
public static interface G1 {}
public static interface G2 {}
}</code>Apply the annotations to the fields and use @Validated (or @Valid ) together with BindingResult in the controller:
<code>@RestController
public class UsersController extends BaseController {
@PostMapping("/valid/save1")
public Object save1(@RequestBody @Validated Users user, BindingResult result) {
Optional<List<String>> op = valid(result);
if (op.isPresent()) return op.get();
return "success";
}
}
public class BaseController {
protected Optional<List<String>> valid(BindingResult result) {
if (result.hasErrors()) {
return Optional.of(result.getAllErrors()
.stream()
.map(err -> err.getDefaultMessage())
.collect(Collectors.toList()));
}
return Optional.empty();
}
}</code>Validation Groups
When the same object is used in different scenarios, groups allow different validation rules.
<code>public class Users {
@NotEmpty(message = "姓名不能为空", groups = G1.class)
private String name;
@Min(value = 10, message = "年龄不能小于 10", groups = G1.class)
private Integer age;
@Min(value = 20, message = "年龄不能小于 20", groups = G2.class)
private Integer age;
@Length(min = 6, max = 18, message = "邮箱介于 6 到 18 之间", groups = {G1.class, G2.class})
private String email;
@NotEmpty(message = "电话必需填写")
private String phone;
public static interface G1 {}
public static interface G2 {}
}</code>Controller usage:
<code>@PostMapping("/valid/save1")
public Object save1(@RequestBody @Validated(Users.G1.class) Users user, BindingResult result) { ... }
@PostMapping("/valid/save2")
public Object save2(@RequestBody @Validated(Users.G2.class) Users user, BindingResult result) { ... }</code>Single Parameter Validation
<code>@RestController
public class UsersController {
@PostMapping("/valid/find")
public Object find(@NotEmpty(message = "参数 Id 不能为空") String id) {
return "查询到参数【" + id + "】";
}
}</code>When validation fails, Spring throws a ConstraintViolationException . A simple handler can return a friendly message:
<code>@ExceptionHandler(ConstraintViolationException.class)
@ResponseBody
public Object handle(ConstraintViolationException e) {
String message = e.getConstraintViolations()
.stream()
.map(ConstraintViolation::getMessage)
.collect(Collectors.joining());
return message;
}</code>Nested Parameter Validation
<code>public class Users {
// ... other fields ...
@Valid
private Address address;
}
public class Address {
@NotEmpty(message = "地址信息必需填写")
private String addr;
}</code>Validate the nested object by adding @Valid on the field.
Internationalization Support
<code>public class Users {
@NotEmpty(message = "{name.notempty}", groups = G1.class)
private String name;
// other fields
}
</code>Create resource bundles ValidationMessages.properties (default) and ValidationMessages_zh_CN.properties , ValidationMessages_en_US.properties with keys such as name.notempty=姓名必需填写 and name.notempty=name is required . Set the request header Accept-Language: en-US to see the English messages.
AOP Unified Validation Handling
<code>@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface EnableValidate {}
@Aspect
@Component
public class ValidateAspect {
@Pointcut("@annotation(com.pack.params.valid.EnableValidate)")
public void valid() {}
@Before("valid()")
public void validateBefore(JoinPoint jp) {
for (Object arg : jp.getArgs()) {
if (arg instanceof BindingResult) {
BindingResult result = (BindingResult) arg;
if (result.hasErrors()) {
String messages = result.getAllErrors()
.stream()
.map(err -> err.getDefaultMessage())
.collect(Collectors.joining(","));
throw new ParamsException(messages);
}
}
}
}
}</code>Apply the annotation to a controller method to trigger the aspect:
<code>@PostMapping("/valid/save1")
@EnableValidate
public Object save1(@RequestBody @Validated(Users.G1.class) Users user, BindingResult result) { ... }</code>Finally, a global exception handler can format the error response uniformly.
Done!
Spring Full-Stack Practical Cases
Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.