本文目录导读:

我来为您详细介绍Java注解校验的实操落地案例,包含完整示例和最佳实践。
注解校验核心组件
自定义注解示例
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.*;
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Constraint(validatedBy = {PhoneValidator.class})
public @interface ValidPhone {
String message() default "手机号格式不正确";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
// 支持自定义正则
String pattern() default "^1[3-9]\\d{9}$";
}
自定义校验器
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
public class PhoneValidator implements ConstraintValidator<ValidPhone, String> {
private String pattern;
@Override
public void initialize(ValidPhone annotation) {
this.pattern = annotation.pattern();
}
@Override
public boolean isValid(String phone, ConstraintValidatorContext context) {
if (phone == null || phone.trim().isEmpty()) {
return false;
}
return phone.matches(pattern);
}
}
实际项目落地案例
实体类使用注解
import javax.validation.constraints.*;
import java.math.BigDecimal;
import java.time.LocalDateTime;
public class UserRequest {
@NotBlank(message = "用户名不能为空")
@Size(min = 2, max = 20, message = "用户名长度2-20个字符")
private String username;
@NotBlank(message = "密码不能为空")
@Pattern(regexp = "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d).{8,20}$",
message = "密码需包含大小写字母和数字,8-20位")
private String password;
@NotNull(message = "年龄不能为空")
@Min(value = 18, message = "年龄必须大于18岁")
@Max(value = 120, message = "年龄不能超过120岁")
private Integer age;
@NotBlank(message = "邮箱不能为空")
@Email(message = "邮箱格式不正确")
private String email;
@ValidPhone(message = "手机号格式不正确")
private String phone;
@Future(message = "过期时间必须是未来时间")
private LocalDateTime expireTime;
@DecimalMin(value = "0.01", message = "金额必须大于0")
@DecimalMax(value = "999999.99", message = "金额不能超过999999.99")
private BigDecimal amount;
// getters and setters
}
Spring Boot集成配置
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.validation.beanvalidation.MethodValidationPostProcessor;
import javax.validation.Validator;
import org.springframework.boot.validation.MessageInterpolatorFactory;
import org.springframework.context.support.ResourceBundleMessageSource;
@Configuration
public class ValidationConfig {
@Bean
public MethodValidationPostProcessor methodValidationPostProcessor() {
MethodValidationPostProcessor processor = new MethodValidationPostProcessor();
processor.setValidator(validator());
return processor;
}
@Bean
public Validator validator() {
ValidatorFactory validatorFactory = Validation.byDefaultProvider()
.configure()
.messageInterpolator(new MessageInterpolatorFactory().getObject())
.buildValidatorFactory();
return validatorFactory.getValidator();
}
}
Controller层使用
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
import javax.validation.constraints.*;
import java.util.List;
@RestController
@RequestMapping("/api/users")
@Validated // 类级别启用校验
public class UserController {
// 方式1:直接校验实体
@PostMapping("/register")
public Result register(@Valid @RequestBody UserRequest request) {
// 业务逻辑
return Result.success();
}
// 方式2:校验路径参数和请求参数
@GetMapping("/{id}")
public Result getUser(
@PathVariable
@Min(value = 1, message = "用户ID必须大于0")
Long id,
@RequestParam(defaultValue = "1")
@Min(value = 1)
Integer page,
@Size(max = 10, message = "每页数量不能超过10")
@RequestParam(defaultValue = "10")
Integer size) {
return Result.success();
}
// 方式3:分组校验
@PostMapping("/update")
public Result updateUser(
@Validated(UpdateGroup.class)
@RequestBody UserRequest request) {
return Result.success();
}
// 方式4:集合参数校验
@PostMapping("/batch")
public Result batchCreate(
@Valid
@Size(min = 1, max = 100, message = "批量操作数量1-100")
@RequestBody List<@Valid UserRequest> users) {
return Result.success();
}
}
分组校验定义
public interface CreateGroup {
// 创建时的校验组
}
public interface UpdateGroup {
// 更新时的校验组
}
// 使用分组
public class UserRequest {
@Null(groups = CreateGroup.class, message = "创建时ID不能指定")
@NotNull(groups = UpdateGroup.class, message = "更新时ID不能为空")
private Long id;
@NotBlank(groups = {CreateGroup.class, UpdateGroup.class})
private String username;
// 更新时不需要校验密码
@NotBlank(groups = CreateGroup.class)
private String password;
}
全局异常处理
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.util.stream.Collectors;
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(MethodArgumentNotValidException.class)
public Result handleValidationExceptions(MethodArgumentNotValidException ex) {
String errors = ex.getBindingResult()
.getFieldErrors()
.stream()
.map(error -> error.getField() + ": " + error.getDefaultMessage())
.collect(Collectors.joining(", "));
return Result.error(400, "参数校验失败: " + errors);
}
@ExceptionHandler(ConstraintViolationException.class)
public Result handleConstraintViolation(ConstraintViolationException ex) {
String errors = ex.getConstraintViolations()
.stream()
.map(ConstraintViolation::getMessage)
.collect(Collectors.joining(", "));
return Result.error(400, "参数校验失败: " + errors);
}
@ExceptionHandler(BindException.class)
public Result handleBindException(BindException ex) {
String errors = ex.getFieldErrors()
.stream()
.map(error -> error.getField() + ": " + error.getDefaultMessage())
.collect(Collectors.joining(", "));
return Result.error(400, "参数绑定失败: " + errors);
}
}
国际化消息配置
// 1. 配置消息源
@Bean
public ResourceBundleMessageSource messageSource() {
ResourceBundleMessageSource source = new ResourceBundleMessageSource();
source.setBasenames("messages/validation");
source.setDefaultEncoding("UTF-8");
return source;
}
// 2. 创建消息文件 resources/messages/validation.properties
# 中文消息
user.username.notblank=用户名不能为空
user.password.pattern=密码格式不正确
# 3. 在注解中使用
@NotBlank(message = "{user.username.notblank}")
private String username;
最佳实践建议
校验层级划分
// 前端校验 - 使用JavaScript/Vue等
// Controller层校验 - 注解校验
// Service层校验 - 业务逻辑校验
// DAO层校验 - 数据库约束
@Service
public class UserService {
// 业务逻辑校验示例
public void createUser(@Valid UserRequest request) {
// 注解校验参数
// 业务校验
if (userRepository.existsByUsername(request.getUsername())) {
throw new BusinessException("用户名已存在");
}
// 复杂业务规则校验
validateBusinessRules(request);
}
private void validateBusinessRules(UserRequest request) {
// 自定义业务规则校验
}
}
自定义校验注解高级示例
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = PasswordMatchesValidator.class)
public @interface PasswordMatches {
String message() default "两次密码输入不一致";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
// 跨字段校验器
public class PasswordMatchesValidator implements ConstraintValidator<PasswordMatches, Object> {
@Override
public boolean isValid(Object obj, ConstraintValidatorContext context) {
if (obj instanceof UserRequest) {
UserRequest user = (UserRequest) obj;
return user.getPassword().equals(user.getConfirmPassword());
}
return false;
}
}
性能优化技巧
// 1. 使用缓存校验配置
@Configuration
public class ValidationConfig {
@Bean
public Validator validator() {
// 复用ValidatorFactory
return Validation.buildDefaultValidatorFactory().getValidator();
}
}
// 2. 按需校验
@Service
public class UserService {
@Autowired
private Validator validator;
public void partialUpdate(UserRequest request) {
// 只校验非空字段
Set<ConstraintViolation<UserRequest>> violations =
validator.validate(request, UpdateGroup.class);
if (!violations.isEmpty()) {
// 处理校验失败
}
}
}
常见问题解决
嵌套对象校验
public class OrderRequest {
@Valid // 嵌套校验
private List<@Valid OrderItem> items;
@Valid
private Address address;
}
public class OrderItem {
@NotNull
@Positive
private Long productId;
@Min(1)
@Max(99)
private Integer quantity;
}
条件校验
// 使用@ScriptAssert进行复杂条件校验
@ScriptAssert(lang = "javascript",
script = "_this.password.equals(_this.confirmPassword)",
message = "密码不一致")
public class UserRequest {
private String password;
private String confirmPassword;
}
通过以上案例,您可以在项目中快速落地Java注解校验,提高代码质量和开发效率,建议根据实际业务需求选择合适的校验方式,并做好异常处理和日志记录。
版权声明:足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播