# bean-validator **Repository Path**: sunskych/beanvalidator ## Basic Information - **Project Name**: bean-validator - **Description**: 基于javabean的校验器 - **Primary Language**: Java - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 2 - **Forks**: 0 - **Created**: 2021-07-11 - **Last Updated**: 2023-12-20 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # bean-validator #### 介绍 基于javabean对象的校验器,适用于校验规则特别多的场景,cpu密集型任务,解决校验规则复用问题、硬编码可维护性。 **功能特点:** ·用文档描述规则,跳出代码层面;便于阅读、维护,无需编译; ·根据java对象自动生成规则文档模版。解决绝大部分工作量问题; ·引入观察者设计模式,便于开发测试过程规则执行结果的快速分析。 **性能优化:** ·动态字节技术,javassist动态字节码,初始化时直接生成代理取值的java字节码方法,以达到空间换时间的目的。 ·依赖规则缓存,避免重复执行; ·无状态应用组件: ·构建无状态组件,一次初始化,重复使用,避免频繁创建对象操作。 ·分析校验行为,确定接口: 校验的值、所在段(对象) 、全局记录(对象)、上下文对象—>方法参数传入; 校验结果—>方法返回值 . **编码要点:** 规则文档在构建验证器时,需要解决的几个要点. ·取值:规则描述的当前属性值。规则依赖的其他属性值 ·逻辑运算:多个依赖规则的逻辑运算条件成立 ·算术运算:规则描述的算术运算。 -> 识别行为(方法),定义角色(接口)。 #### 调研 1. 取值及取值表达式,程序如何获取到值?反射或者是现有SpringEL表达式工具?注意性能 取值性能分析 | 压测笔数 | 硬编码耗时,毫秒 | 反射耗时 | springel | | -------- | ---------------- | -------- | -------- | | 10万 | 10 | 15 | 1012 | | 100万 | 25 | 42 | 3759 | | 1000万 | 20 | 105 | 19984 | | 1亿 | 13 | 778 | 208127 | | 10亿 | 26 | 4392 | | 考虑适应百万量级数据,且每条数据规则上百条以上的场景,会对cpu进行密集型资源消耗。为应对此场景,采用动态字节码技术模拟硬编码达到最优。 2. 算术表达式,如何实现算术表达式逻辑。5+9*3 | 压测笔数 | rpn自实现,cost mill | google aviator | | | -------- | -------------------- | -------------- | ---- | | 10万 | 268 | 346 | | | 100万 | 1396 | 1607 | | | 1000万 | 12480 | 12626 | | | 1亿 | 124006 | 122563 | | | 10亿 | 1293296 | 1324115 | | cpu、内存指标稳定; #### 软件架构 ##### 软件设计说明 ​ **1 设计规则文档。** ​ 1.1 结构问题:与对象结构一致。-->对应xml元素包含结构。 ​ 1.2 如何描述规则问题:抽象出规则的概念,可描述规则通用信息。概念上对规则进行分析,从而对规则进行分类定义规则类型。然后在对具体的规则类型进行描述。 ( 抽象,提取规则类型,明确范围,规则描述; 属性+规则类型+规则描述-->构成一个完整的规则。) ​ **示例:** ```xml ``` ​ 1.3 示例,规则概念对应 ;rule元素与子元素proerty一起完成规则描述。 ​ ​ **2 规则___细节描述的内容如何设计??** ​ 1.1 作为规则时,描述内容该如何落地? ​ 一般的'操作'、'逻辑'、'常量' 等静态说明,都都可以通过 方式注入。 ​ 重点如解决非静态描述问题,非静态主要有动态取值和算术运算问题,下面我将结合场景进行分析及设计。 ​ 1.1.1 规则描述中需要动态属性值,或预系统中预设置的内容(比如参数,字典缓存等)。 ​ a. 可以获取在上下文中预设置内容,定义特定取值表达式和关键词进行描述。 ​ b. 从校验的全局对象中,通过属性名称作为关键词(全局路径)进行描述, ​ c. 从校验属性的所属对象中,通过属性名称作为关键词(单级子属性名)进行描述。已经有全局路径取值还有必要单级子属性名取值吗?实际的应用场景中可能 涉及到一个集合中对应的属性之间描述依赖,故而需要。 ​ 1.1.2 规则描述中设计算术运算 ​ a. 常量 算术运算符 常量 ​ b. 动态取值 算术运算符 常量 ​ c. 动态取值 算术运算符 动态取值 ​ 1.2 作为被依赖条件时,该如何解决校验属性v取值问题? ​ a. 取到这个被依赖规则的对应属性的值? 根据属性的结构关系,可获取到它的已知全局路径($bean.xxx.xx)关键词。 ​ b. 集合中对象属性规则被同对象中的其他属性依赖场景:单级子属性名方式可以解决($local.xxx)。全局路径方式无法适用,原因是集合中多个对象,无法确定是哪一个对象的属性的规则; ​ c. 集合中对象属性规则被其他对象中的属性依赖场景:不论是全局还是单级子属性名方式都无法解决,但是这种场景实际中是否存在呢?(仔细想好像可能性极小)。但是也要考虑到实在无法实现的复杂场景。引入超级扩展方式(手写java代码)。 ​ ​ **3 编码设计** ​ **构建过程** ​ a 扫描规则文件路径; 解析为元数据; 定义过程,数据规范基本检查与准备工作; ​ b 构建Ichecker; 依据规则描述创建checker及行为。 ​ c 构建执行引擎fieldValidator;依据描述结构field和rule(checker)加入fieldValidator中。放入缓存FieldValidatorCache中。 ​ d 在构建时,同时需要生成不同的功能的处理者或者代理者,比如各种取值处理者、依赖处理者。 ​ d 提供工厂根据id获取验证器实例,以供用户使用。 ​ **接口设计** ​ 1.1 元数据:ValidateRules、ValidateRule、Field、FieldRule、Property ​ 1.2 执行组件接口: ​ a IChecker rule对应检查器-->职责: ​ 作为角色时的规则执行者(输入:校验的值、所在对象,全局对象,上下文对象{可预设系统参数,在实行器内部根据key获取} ;输出:反馈对象 ); ​ 作为条件时的条件执行者(输入:校验的值、所在对象,全局对象;输出:boolean是否成立 ) ​ b FieldValidator java对象规则集对应的验证器-->职责: 规则集的执行引擎。(概念上同class的属性)规则引擎实现、v取值实现,组合模式。 ​ 1.3 引擎部件接口? ​ a IValidator\BeanValidator(对FieldValidator进行包装,屏蔽细节,对用户提供服务);>职责: 规则集的执行引擎。(输入:java对象;输出:反馈对象集合)。 ​ 1.4 一个checker实现 ​ a 解决《2 规则___细节描述的内容如何设计》的实现。 ​ 1.5 组件创建接口 ​ a IValidatorRulesParser-->职责:规则内容解析为实体元数据对象。1.规则原始形态对象;2.规则文件解析为原形态实体对象结构。 ​ b IValidatorRuleDefiner-->职责: 明确验证器规则描述实体对象;基本规范检查和准备工作。 ​ c ValidatorBulider-->职责:核心代码,将一个规则描述实体创建为一个FieldValidator规则执行引擎。 ​ d ValidatorLoader-->职责:上述加载过程。 ​ **代码细节** ·识别职责(行为)—>划分角色(类) ·要想清楚运行时状态时或者周生命周期—>代码实现方式 · 所有的运行代码都应是无状态的,错误代码、规则本身的表达数据(规则描述、依赖)在创建阶段赋值后不在变化。 ··有状态的数据:校验的值、所在对象,全局对象—>方法参数传入;错误返回结果—>返回值 . #### ##### 软件内嵌机制 1. 当一个规则不满足时,是否继续执行该field的其他规则(策略todo)。 2. 当一个javabean中的校验错误达到最大错误数时,不再继续执行(上下文中允许最大错误数设置决定)。 安装教程 xxxx xxxx xxxx **使用说明** ​ **语法说明书:** ​ src/main/resources/validator说明书.xlsx ​ **样例:** ​ src/test/java/com/ssky/demo/TestMain.java ​ src/test/resources/earth-validator.xml xxxx xxxx xxxx 参与贡献 Fork 本仓库 新建 Feat_xxx 分支 提交代码 新建 Pull Request