Validator自定义注解

原视频:

项目结构建议

src/
├── main/
│   ├── java/
│   │   └── com.example.demo/
│   │       ├── DemoApplication.java  // Spring Boot 主类
│   │       ├── annotation/
│   │       │   └── Phone.java  // 自定义注解
│   │       ├── validator/
│   │       │   └── PhoneValidator.java  // 校验器
│   │       ├── controller/
│   │       │   └── UserController.java  // 接口控制器
│   │       └── handler/
│   │           └── GlobalExceptionHandler.java  // 异常处理器
│   └── resources/
│       └── application.yml  // 配置(可选)
pom.xml  // Maven 配置文件

1. pom.xml (Maven 依赖)

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.example</groupId>
    <artifactId>demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>demo</name>
    <description>Demo project for custom validation</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.1.0</version>  <!-- 根据需要调整版本 -->
        <relativePath/>
    </parent>

    <properties>
        <java.version>17</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>
  • 下载方式:复制到本地文件名为 pom.xml,然后运行 mvn clean install构建项目。

2. Phone.java

(自定义注解,路径:src/main/java/com/example/demo/annotation/Phone.java)

package com.example.demo.annotation;

import com.example.demo.validator.PhoneValidator;
import jakarta.validation.Constraint;
import jakarta.validation.Payload;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = PhoneValidator.class)
public @interface Phone {
    String message() default "无效的手机号码格式";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}
  • 注意:使用 jakarta.validation(Spring Boot 3+ 默认),如果使用旧版可替换为 javax.validation

3. PhoneValidator.java

(校验逻辑,路径:src/main/java/com/example/demo/validator/PhoneValidator.java)

package com.example.demo.validator;

import com.example.demo.annotation.Phone;
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
import java.util.regex.Pattern;

public class PhoneValidator implements ConstraintValidator<Phone, String> {
    private static final Pattern PHONE_PATTERN = Pattern.compile("^1[3-9]\\d{9}$");

    @Override
    public boolean isValid(String phone, ConstraintValidatorContext context) {
        if (phone == null) {
            return false;
        }
        return PHONE_PATTERN.matcher(phone).matches();
    }
}

4. UserController.java

(接口使用,路径:src/main/java/com/example/demo/controller/UserController.java)

package com.example.demo.controller;

import com.example.demo.annotation.Phone;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

@RestController
@Validated
public class UserController {
    @PostMapping("/user")
    public String createUser(@Phone @RequestBody String phone) {
        return "手机号码合法: " + phone;
    }
}
  • 注意:对于@RequestBody参数,校验需在DTO对象上使用注解。这里简化示例;实际中建议用DTO类包裹参数。

5. GlobalExceptionHandler.java

(错误处理,路径:src/main/java/com/example/demo/handler/GlobalExceptionHandler.java)

package com.example.demo.handler;

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

@RestControllerAdvice
public class GlobalExceptionHandler {
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<String> handleValidationException(MethodArgumentNotValidException ex) {
        String errorMessage = ex.getBindingResult().getFieldError().getDefaultMessage();
        return new ResponseEntity<>(errorMessage, HttpStatus.BAD_REQUEST);
    }

    // 对于ConstraintViolationException(方法参数校验)的处理
    @ExceptionHandler(jakarta.validation.ConstraintViolationException.class)
    public ResponseEntity<String> handleConstraintViolation(jakarta.validation.ConstraintViolationException ex) {
        String errorMessage = ex.getConstraintViolations().iterator().next().getMessage();
        return new ResponseEntity<>(errorMessage, HttpStatus.BAD_REQUEST);
    }
}
  • 扩展:添加了对 ConstraintViolationException的处理,以支持方法级校验。

6. DemoApplication.java

(Spring Boot 主类,路径:src/main/java/com/example/demo/DemoApplication.java)

package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

7. application.yml

(可选配置,路径:src/main/resources/application.yml)

server:
  port: 8080
spring:
  application:
    name: validation-demo

使用说明

  1. 创建项目:使用Spring Initializr(https://start.spring.io/)生成基础项目,或手动创建文件夹并粘贴代码。

  2. 运行测试

    • 启动应用:运行 DemoApplication.main()
    • 测试接口:使用Postman发送POST请求到 http://localhost:8080/user,body为raw text如"13812345678"(合法)或无效号码。
    • 预期:合法返回成功消息,非法返回400错误和提示。
  3. 扩展建议:在实际项目中,将注解应用于DTO字段而非直接参数,以支持复杂对象校验。