由 Gemini-3.0-pro-preview 生成
Spring Core的Validation主要基于 JSR-380 (Bean Validation 2.0/3.0) 标准。Spring本身不直接实现校验逻辑,而是提供了一套机制来集成第三方的实现(通常是 Hibernate Validator)。
下面是一个完全不使用Spring Boot,仅使用Spring Framework 6 + Hibernate Validator的完整教程。
1. 理解核心概念
在开始写代码前,你需要明白三个角色的关系:
- JSR-380 API (Jakarta Validation) : 定义了校验的接口和注解(如
@NotNull,@Size)。这只是标准,没有代码逻辑。 - Hibernate Validator : 它是JSR-380的参考实现。真正干活(检查字段是否为空)的是它。
- Spring Core : 它是粘合剂 。通过
LocalValidatorFactoryBean将 Hibernate Validator 集成到 Spring 容器中,让你能在代码里轻松注入并使用。
2. 环境搭建 (Maven依赖)
由于没有Spring Boot Starter,我们需要手动引入所有必要的包。
注意:这里我们使用 Spring 6 和 Jakarta EE (现代标准)。
pom.xml:
xml
<dependencies>
<!-- 1. Spring Context (Spring的核心) -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>6.1.3</version>
</dependency>
<!-- 2. Jakarta Validation API (标准接口) -->
<dependency>
<groupId>jakarta.validation</groupId>
<artifactId>jakarta.validation-api</artifactId>
<version>3.0.2</version>
</dependency>
<!-- 3. Hibernate Validator (校验器的具体实现) -->
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>8.0.1.Final</version>
</dependency>
<!-- 4. Expression Language (EL表达式支持) -->
<!-- Hibernate Validator 依赖 EL 表达式来解析错误信息,Spring Boot内置了,但Core通过Main方法运行需要手动加 -->
<dependency>
<groupId>org.glassfish</groupId>
<artifactId>jakarta.el</artifactId>
<version>4.0.2</version>
</dependency>
</dependencies>
3. 定义待校验的实体 (Java Bean)
创建一个普通的User类,使用标准注解。
java
package com.example.demo.model;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
public class User {
@NotBlank(message = "用户名不能为空")
private String username;
@Size(min = 6, max = 20, message = "密码长度必须在6-20之间")
private String password;
@Min(value = 18, message = "未成年人禁止注册")
private int age;
@Email(message = "邮箱格式不正确")
private String email;
// Getters and Setters (必须有,否则Validation无法读取字段值)
public String getUsername() { return username; }
public void setUsername(String username) { this.username = username; }
public String getPassword() { return password; }
public void setPassword(String password) { this.password = password; }
public int getAge() { return age; }
public void setAge(int age) { this.age = age; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
}
4. Spring 配置类
这是脱离Spring Boot最关键的一步。我们需要手动定义校验器的Bean。
核心类是:LocalValidatorFactoryBean。它既实现了Spring的校验接口,也实现了Jakarta的校验接口,会在启动时自动检测Classpath下的Hibernate Validator。
java
package com.example.demo.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
import org.springframework.validation.beanvalidation.MethodValidationPostProcessor;
@Configuration
@ComponentScan(basePackages = "com.example.demo")
public class AppConfig {
/**
* 配置全局校验器工厂
* 作用:让Spring识别Hibernate Validator,并将其注册到容器中。
*/
@Bean
public LocalValidatorFactoryBean validator() {
return new LocalValidatorFactoryBean();
}
/**
* 开启方法级别的校验支持 (可选,但在Service层校验很有用)
* 作用:支持在方法参数上使用 @NotNull, @Min 等注解,
* 以及在类上使用 @Validated 注解。
*/
@Bean
public MethodValidationPostProcessor methodValidationPostProcessor() {
MethodValidationPostProcessor processor = new MethodValidationPostProcessor();
processor.setValidator(validator());
return processor;
}
}
5. 两种使用方式
在后端开发中,通常有两种校验场景:
- 手动校验:在代码里显式调用校验逻辑(类似于Controller接收到JSON后)。
- 方法级校验 (AOP):调用Service方法时自动拦截校验参数。
方式一:手动注入 Validator 进行校验
创建一个Service,模拟处理业务逻辑。
java
package com.example.demo.service;
import com.example.demo.model.User;
import jakarta.validation.ConstraintViolation;
import jakarta.validation.Validator; // 注意是 jakarta 包
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Set;
@Service
public class UserService {
@Autowired
private Validator validator; // 注入我们在AppConfig里配置的LocalValidatorFactoryBean
public void registerUser(User user) {
// 1. 执行校验
// validate方法会返回一个Set,包含所有违规信息。如果Set为空,说明校验通过。
Set<ConstraintViolation<User>> violations = validator.validate(user);
// 2. 判断结果
if (!violations.isEmpty()) {
// 有错误,打印出来或抛出异常
for (ConstraintViolation<User> violation : violations) {
System.out.println("校验失败: " + violation.getPropertyPath() + " " + violation.getMessage());
}
throw new IllegalArgumentException("参数校验失败");
}
System.out.println("用户 " + user.getUsername() + " 注册成功!");
}
}
方式二:方法级别的自动校验 (@Validated)
这种方式利用了Spring AOP,通过 MethodValidationPostProcessor 实现。
java
package com.example.demo.service;
import jakarta.validation.constraints.NotNull;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
@Service
@Validated // 1. 告诉Spring这个类需要被AOP代理进行校验
public class OrderService {
// 2. 直接在参数前加注解
public void createOrder(@NotNull(message = "订单ID不能为空") String orderId) {
System.out.println("订单 " + orderId + " 创建成功");
}
}
6. 运行测试 (Main方法)
最后,我们通过 AnnotationConfigApplicationContext 启动Spring容器来测试一下。
java
package com.example.demo;
import com.example.demo.config.AppConfig;
import com.example.demo.model.User;
import com.example.demo.service.OrderService;
import com.example.demo.service.UserService;
import jakarta.validation.ConstraintViolationException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Main {
public static void main(String[] args) {
// 1. 启动 Spring 容器
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
// --- 测试 1: 手动校验 (UserService) ---
System.out.println("--- 测试 1: 手动校验 ---");
UserService userService = context.getBean(UserService.class);
User user = new User();
user.setUsername(""); // 违规:NotBlank
user.setAge(10); // 违规:Min(18)
try {
userService.registerUser(user);
} catch (Exception e) {
System.out.println("捕获异常: " + e.getMessage());
}
// --- 测试 2: 方法级自动校验 (OrderService) ---
System.out.println("\n--- 测试 2: 方法级自动校验 ---");
OrderService orderService = context.getBean(OrderService.class);
try {
orderService.createOrder(null); // 传入null,触发 @NotNull
} catch (ConstraintViolationException e) {
System.out.println("捕获方法校验异常: " + e.getMessage());
}
}
}
总结 (Junior Developer 知识点复盘)
-
为什么没有Spring Boot也能跑? Spring Boot只是帮你把
LocalValidatorFactoryBean自动配置好了(放在了ValidationAutoConfiguration类里)。在这里,我们在AppConfig里自己new了一个,效果是一样的。 -
jakarta.validationvsorg.springframework.validation- 我们在实体类上用的是
jakarta.validation(JSR标准)。 - Spring也有自己的
org.springframework.validation.Validator接口,但LocalValidatorFactoryBean很聪明,它同时实现了这两个接口。最佳实践是使用标准的 JSR 注解。
- 我们在实体类上用的是