ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

【Java学习】自定义注解的使用,校验 Excel 上传文件

2026/9/15 7:50:03 拓冰建站 浏览量
【Java学习】自定义注解的使用,校验 Excel 上传文件 基础概念一、引入依赖dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-validation/artifactId /dependency验证注解都在javax.validation.constraints这个包下二、定义自定义注解 XlsxFile项目中的注解定义在XlsxFile.javaDocumented Target({ ElementType.FIELD, ElementType.PARAMETER }) Retention(RetentionPolicy.RUNTIME) Constraint(validatedBy XlsxFileValidator.class) public interface XlsxFile { String message() default 请上传有效的 Excel 文件; long maxSize() default 5 * 1024 * 1024; Class?[] groups() default {}; Class? extends Payload[] payload() default {}; }Constraint(validatedBy XlsxFileValidator.class)这是自定义校验注解最关键的配置。当遇到XlsxFile时使用XlsxFileValidator执行具体校验。注解负责声明规则Validator 负责实现规则。message、groups和payload一个标准的 Bean Validation 约束注解通常需要提供String message() default ...; Class?[] groups() default {}; Class? extends Payload[] payload() default {};它们的作用分别是message默认校验失败信息groups校验分组可以针对不同业务场景执行不同校验payload携带额外元数据普通项目中较少直接使用。三、实现自定义校验器校验器位于XlsxFileValidator.javapublic class XlsxFileValidator implements ConstraintValidatorXlsxFile, MultipartFile { private long maxSize; Override public void initialize(XlsxFile annotation) { this.maxSize annotation.maxSize(); } Override public boolean isValid( MultipartFile file, ConstraintValidatorContext context) { if (file null || file.isEmpty()) { return fail(context, 请选择要上传的 Excel 文件); } if (file.getSize() maxSize) { return fail(context, Excel 文件不能超过 5 MB); } String filename file.getOriginalFilename(); if (filename null || !filename.toLowerCase(Locale.ROOT) .endsWith(.xlsx)) { return fail(context, 仅支持 .xlsx 文件); } return true; } private boolean fail( ConstraintValidatorContext context, String message) { context.disableDefaultConstraintViolation(); context.buildConstraintViolationWithTemplate(message) .addConstraintViolation(); return false; } }1.ConstraintValidator的两个泛型ConstraintValidatorXlsxFile, MultipartFile第一个泛型表示对应的注解类型XlsxFile第二个泛型表示被校验的数据类型MultipartFile因此这个校验器用于校验标注了XlsxFile的MultipartFile数据。2.initialize()方法Override public void initialize(XlsxFile annotation) { this.maxSize annotation.maxSize(); }该方法用于读取注解上的配置。例如XlsxFile(maxSize 10 * 1024 * 1024)那么annotation.maxSize()得到的就是 10 MB。3.isValid()方法isValid()是真正执行校验的方法Override public boolean isValid( MultipartFile file, ConstraintValidatorContext context) { }返回值含义返回true校验通过返回false校验失败。当前项目依次校验了文件是否为空、文件大小以及文件后缀。4. 自定义错误信息如果直接返回false框架会使用注解的默认message。项目中希望不同错误返回不同提示因此使用context.disableDefaultConstraintViolation(); context.buildConstraintViolationWithTemplate(message) .addConstraintViolation();这样便可以分别返回请选择要上传的 Excel 文件Excel 文件不能超过 5 MB仅支持 .xlsx 文件四、在 DTO 中使用注解项目中的请求对象是UserImportRequest.javapublic class UserImportRequest { XlsxFile(maxSize 5 * 1024 * 1024) private MultipartFile file; public MultipartFile getFile() { return file; } public void setFile(MultipartFile file) { this.file file; } }六、使用Valid触发校验仅仅在字段上添加XlsxFile还不够还需要在 Controller 参数前添加ValidPostMapping( value /import, consumes MediaType.MULTIPART_FORM_DATA_VALUE ) public ImportResult importUsers( Valid ModelAttribute UserImportRequest request) { MultipartFile file request.getFile(); try (InputStream inputStream file.getInputStream()) { return userService.importUsers(inputStream); } catch (IOException exception) { throw new UserImportException( 0, 文件读取失败, List.of(new ImportError( 0, file, 无法读取上传的文件 )) ); } }其中ModelAttribute将multipart/form-data请求参数绑定到 DTOValid触发 DTO 字段上的 Bean Validation 校验校验通过后才会进入方法主体校验失败时会抛出参数校验异常。这里很容易忘记Valid。如果没有它字段上的XlsxFile通常不会自动执行。七、统一处理校验异常项目通过GlobalExceptionHandler.java统一处理参数校验异常ExceptionHandler(MethodArgumentNotValidException.class) public ResponseEntityImportResult handleValidationException( MethodArgumentNotValidException exception) { ListImportError errors exception .getBindingResult() .getFieldErrors() .stream() .map(error - new ImportError( 0, error.getField(), error.getDefaultMessage() )) .toList(); ImportResult result ImportResult.failure( 0, 文件校验失败, errors ); return ResponseEntity.badRequest().body(result); }这里通过exception.getBindingResult().getFieldErrors()获取所有字段错误再转换成项目统一的ImportError对象。这样Controller不需要编写大量if/else客户端收到的异常结构也更加统一。八、学习总结自定义注解这种声明式写法更加直观也更容易复用和维护。自定义校验注解的通用实现步骤可以总结为引入Validation依赖创建自定义注解使用Constraint绑定校验器实现ConstraintValidator在 DTO 字段上使用注解通过Valid触发校验使用全局异常处理器统一返回错误。这套模式不只适用于 Excel 文件还可以扩展到身份证号、手机号、枚举值、日期范围、业务编码以及多个字段之间的关联校验。