注解+反射 参数校验更加简洁

2024-06-24 01:08

本文主要是介绍注解+反射 参数校验更加简洁,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

背景

做RPC接口的时候 我们需要对一些字段做非空校验 在字段很多的情况下 如果一个一个的用if判断 代码会很恶心 所以我们需要有一种便捷的方式去实现这个功能 比如使用注解+反射的方式

怎么做?

首先定义注解
非空注解:

package com.api.annotation;import java.lang.annotation.*;/*** 非空校验注解*/@Target({ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface NotNull {String message() default "";
}

数值注解:

package com.api.annotation;import java.lang.annotation.*;/*** 数字类型校验注解*/@Target({ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Number {String message() default "";
}

日期注解:

package com.api.annotation;import java.lang.annotation.*;/*** 日期格式校验注解*/@Target({ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Date {String message() default "";String format() default "yyyy-MM-dd HH:mm:ss";
}

接下来定义一下校验的返回值

public class BaseResultInfo {/*** 响应码*/protected int code = 200;/*** 响应消息*/protected String msg = "OK";public int getCode() {return code;}public void setCode(int code) {this.code = code;}public String getMsg() {return msg;}public void setMsg(String msg) {this.msg = msg;}

再定义一个返回码的枚举:

public enum ResultCodeEnum {SUCCESS(200,"成功"),PARAM_ERROR(1,"参数异常!!"),SYSTEM_ERROR(2,"系统异常!");ResultCodeEnum(int code, String msg) {this.code = code;this.msg = msg;}/*** 返回code*/private int code;/*** 错误消息*/private String msg;public int getCode() {return code;}public void setCode(int code) {this.code = code;}public String getMsg() {return msg;}public void setMsg(String msg) {this.msg = msg;}
}

接下来定义一个基类 包含一个校验方法 通过反射来对字段进行校验

@Service
public class BaseService {/*** 验证某个bean的参数** @param object 被校验的参数*//*** 验证某个bean的参数* @param object        校验bean对象* @param resultInfo    校验结果* @param <T>* @return* @throws Exception*/public <T> BaseResultInfo validate(T object, BaseResultInfo resultInfo) throws Exception {if(object == null){resultInfo.setCode(ResultCodeEnum.PARAM_ERROR.getCode());resultInfo.setMsg("入参对象不能为null");return resultInfo;}//获取object的类型Class<? extends Object> clazz = object.getClass();//获取该类型声明的成员Field[] fields = clazz.getDeclaredFields();//遍历属性for (Field field : fields) {//对于private私有化的成员变量,通过setAccessible来修改器访问权限field.setAccessible(true);if(!validate(field, object, resultInfo)){return resultInfo;};//重新设置会私有权限field.setAccessible(false);}return resultInfo;}/*** 校验bean对象注解* @param field* @param object* @param resultInfo* @throws Exception*/private <T> boolean validate(Field field, T object, BaseResultInfo resultInfo) throws Exception {if(field.isAnnotationPresent(NotNull.class)){NotNull annotation = field.getAnnotation(NotNull.class);if(field.get(object) == null || StringUtils.isBlank(field.get(object).toString())){resultInfo.setCode(ResultCodeEnum.PARAM_ERROR.getCode());resultInfo.setMsg(StringUtils.isEmpty(annotation.message()) ? field.getName()+"不可为空" : annotation.message() );return false;}}if(field.isAnnotationPresent(Date.class)){Date annotation = field.getAnnotation(Date.class);SimpleDateFormat format = new SimpleDateFormat(annotation.format());try {if(field.get(object) != null){format.parse(field.get(object).toString());}} catch (ParseException e) {resultInfo.setCode(ResultCodeEnum.PARAM_ERROR.getCode());resultInfo.setMsg(annotation.message());return false;}}if(field.isAnnotationPresent(Number.class)){Number annotation = field.getAnnotation(Number.class);if(field.get(object) != null){try{new BigDecimal(field.get(object).toString());}catch (Exception e){resultInfo.setCode(ResultCodeEnum.PARAM_ERROR.getCode());resultInfo.setMsg(StringUtils.isEmpty(annotation.message()) ? field.getName()+"必须是数值" : annotation.message() );return false;}}}return true;}}

准备工作做好了 接下来看下怎么用
随便定义一个方法的入参,对于thrift等接口,数值类型如果使用double会丢失精度,而且也不支持date类型,所以我们一般都是使用String类型(这里仅仅是参考 没有加thrift的相关注解)

import com.api.annotation.Date;
import com.api.annotation.NotNull;
import com.api.annotation.Number;public class ApplyRequest {@NotNullprivate String applyNum;@NotNull(message = "描述不可以为空")private String description;@Numberprivate String amount;@Date(format = "yyyy-MM")private String periodDate;@Dateprivate String creationDate;public String getApplyNum() {return applyNum;}public void setApplyNum(String applyNum) {this.applyNum = applyNum;}public String getAmount() {return amount;}public void setAmount(String amount) {this.amount = amount;}public String getPeriodDate() {return periodDate;}public void setPeriodDate(String periodDate) {this.periodDate = periodDate;}public String getCreationDate() {return creationDate;}public void setCreationDate(String creationDate) {this.creationDate = creationDate;}
}

然后我们只需要让接口的实现类继承BaseService类,然后在方法内部调用父类的validate方法即可进行参数的一些基础校验

这篇关于注解+反射 参数校验更加简洁的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1088765

相关文章

springboot自定义注解RateLimiter限流注解技术文档详解

《springboot自定义注解RateLimiter限流注解技术文档详解》文章介绍了限流技术的概念、作用及实现方式,通过SpringAOP拦截方法、缓存存储计数器,结合注解、枚举、异常类等核心组件,... 目录什么是限流系统架构核心组件详解1. 限流注解 (@RateLimiter)2. 限流类型枚举 (

SpringBoot 异常处理/自定义格式校验的问题实例详解

《SpringBoot异常处理/自定义格式校验的问题实例详解》文章探讨SpringBoot中自定义注解校验问题,区分参数级与类级约束触发的异常类型,建议通过@RestControllerAdvice... 目录1. 问题简要描述2. 异常触发1) 参数级别约束2) 类级别约束3. 异常处理1) 字段级别约束

Java中使用 @Builder 注解的简单示例

《Java中使用@Builder注解的简单示例》@Builder简化构建但存在复杂性,需配合其他注解,导致可变性、抽象类型处理难题,链式编程非最佳实践,适合长期对象,避免与@Data混用,改用@G... 目录一、案例二、不足之处大多数同学使用 @Builder 无非就是为了链式编程,然而 @Builder

spring中的@MapperScan注解属性解析

《spring中的@MapperScan注解属性解析》@MapperScan是Spring集成MyBatis时自动扫描Mapper接口的注解,简化配置并支持多数据源,通过属性控制扫描路径和过滤条件,利... 目录一、核心功能与作用二、注解属性解析三、底层实现原理四、使用场景与最佳实践五、注意事项与常见问题六

Spring Boot spring-boot-maven-plugin 参数配置详解(最新推荐)

《SpringBootspring-boot-maven-plugin参数配置详解(最新推荐)》文章介绍了SpringBootMaven插件的5个核心目标(repackage、run、start... 目录一 spring-boot-maven-plugin 插件的5个Goals二 应用场景1 重新打包应用

SpringBoot中如何使用Assert进行断言校验

《SpringBoot中如何使用Assert进行断言校验》Java提供了内置的assert机制,而Spring框架也提供了更强大的Assert工具类来帮助开发者进行参数校验和状态检查,下... 目录前言一、Java 原生assert简介1.1 使用方式1.2 示例代码1.3 优缺点分析二、Spring Fr

java中反射Reflection的4个作用详解

《java中反射Reflection的4个作用详解》反射Reflection是Java等编程语言中的一个重要特性,它允许程序在运行时进行自我检查和对内部成员(如字段、方法、类等)的操作,本文将详细介绍... 目录作用1、在运行时判断任意一个对象所属的类作用2、在运行时构造任意一个类的对象作用3、在运行时判断

Java内存分配与JVM参数详解(推荐)

《Java内存分配与JVM参数详解(推荐)》本文详解JVM内存结构与参数调整,涵盖堆分代、元空间、GC选择及优化策略,帮助开发者提升性能、避免内存泄漏,本文给大家介绍Java内存分配与JVM参数详解,... 目录引言JVM内存结构JVM参数概述堆内存分配年轻代与老年代调整堆内存大小调整年轻代与老年代比例元空

Spring如何使用注解@DependsOn控制Bean加载顺序

《Spring如何使用注解@DependsOn控制Bean加载顺序》:本文主要介绍Spring如何使用注解@DependsOn控制Bean加载顺序,具有很好的参考价值,希望对大家有所帮助,如有错误... 目录1.javascript 前言2. 代码实现总结1. 前言默认情况下,Spring加载Bean的顺

Spring @Scheduled注解及工作原理

《Spring@Scheduled注解及工作原理》Spring的@Scheduled注解用于标记定时任务,无需额外库,需配置@EnableScheduling,设置fixedRate、fixedDe... 目录1.@Scheduled注解定义2.配置 @Scheduled2.1 开启定时任务支持2.2 创建