├── .gitignore ├── LICENSE ├── README.md ├── changelog.md ├── pom.xml └── src ├── main ├── java │ └── com │ │ └── github │ │ └── liangbaika │ │ └── validate │ │ ├── annations │ │ ├── AbcValidate.java │ │ ├── ValidateParam.java │ │ └── ValidateParams.java │ │ ├── config │ │ └── SpringValidateAutoConfig.java │ │ ├── core │ │ ├── AbcValidator.java │ │ ├── CheckParamAop.java │ │ ├── ParamValidator.java │ │ └── ValidateBuilder.java │ │ ├── enums │ │ └── Check.java │ │ ├── exception │ │ ├── ParamsCheckException.java │ │ └── ParamsInValidException.java │ │ └── utils │ │ ├── CheckUtil.java │ │ └── SpringContextHolder.java └── resources │ └── META-INF │ └── spring.factories └── test └── java └── com └── github └── liangbaika └── validate └── test └── Tests.java /.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | !.mvn/wrapper/maven-wrapper.jar 3 | 4 | ### STS ### 5 | .apt_generated 6 | .classpath 7 | .factorypath 8 | .project 9 | .settings 10 | .springBeans 11 | .sts4-cache 12 | .mvn 13 | .idea 14 | mvnw* 15 | *.iml 16 | ### IntelliJ IDEA ### 17 | .idea 18 | *.iws 19 | *.iml 20 | *.ipr 21 | 22 | ### NetBeans ### 23 | /nbproject/private/ 24 | /build/ 25 | /nbbuild/ 26 | /dist/ 27 | /nbdist/ 28 | /.nb-gradle/ 29 | 30 | # Compiled class file 31 | *.class 32 | *.iml 33 | *.idea 34 | target/ 35 | logs/ 36 | 37 | # Log file 38 | *.log 39 | 40 | # BlueJ files 41 | *.ctxt 42 | 43 | # Mobile Tools for Java (J2ME) 44 | .mtj.tmp/ 45 | 46 | # Package Files # 47 | *.jar 48 | *.war 49 | *.ear 50 | *.zip 51 | *.tar.gz 52 | *.rar 53 | 54 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 55 | hs_err_pid* 56 | 57 | *velocity.log* 58 | 59 | # Eclipse # 60 | .classpath 61 | .project 62 | .settings/ 63 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # validate-spring-boot-starter 2 | 3 | # latest=1.2.2 4 | 5 | 6 | # 中央仓库 7 | ``` 8 | maven 9 | 10 | com.github.liangbaika 11 | validate-spring-boot-starter 12 | {latest} 13 | 14 | 15 | gradle 16 | compile group: 'com.github.liangbaika', name: 'validate-spring-boot-starter', version: '{latest}' 17 | ``` 18 | 19 | # validate-springboot-starter 简介 (desc) 20 | 是一个validate-spring-boot-starter,与springboot框架无缝集成的灵活丰富的验证框架。 21 | 完全兼容javax-validation 和 hibernate Validation,并比他们更灵活 简单 强大 22 | versus springboot Framework for seamless integration of verification framework. 23 | 24 | # 优点(advantages) 25 | 1. 内置常用验证 26 | 2. 对 javax validation 和hibernate-validate **完全兼容** 27 | 3. 支持验证Bean可**重用** 28 | 4. 支持条件分组 支持自定义验证器 (自定义**更简单强大**,可做到和业务**完全解耦**) 29 | 5. 多条件操作符 30 | 6. 聚合功能 31 | 7. 错误信息提示**更加友好** 更准确 32 | 8. **简单 灵活 无脑** 33 | 34 | 常用验证:比如手机号验证,正则验证,ip,邮箱,长度,范围,数字,小数,中国车牌号,身份证,长度, 35 | url, 图书ISBN编号,文件后缀,文件大小 等常用验证。 36 | 37 | Integrated with a lot of authentication, such as phone number authentication, regular authentication, email, 38 | Numbers, decimals, license plate number, ID card, length, URL,ISBN and so on.Our beans are reusable and have more 39 | flexibility than Javax Validation. 40 | 41 | # 自定义(custom) 42 | 只需要实现 **_ParamValidator_** 接口就好了,便可以处理复杂的验证,和业务代码完全解耦(你需要让这个实现接口的Bean**被Spring容器托管**); 43 | All you need to do is implement the ParamValidator interface, which handles complex validation and is completely 44 | decoupled from the business code (you need to have the Bean that implements the interface hosted by the Spring container). 45 | 46 | # 注意(attention) 47 | 1. 如果非法参数将抛出 **_ParamsInValidException_**,您应该捕获这个特殊的异常并解决它。 48 | 如果采用的是jsr303型即javax-validation验证 则需要自行处理异常(**org.springframework.web.bind.MethodArgumentNotValidException**)。 49 | 对象多级验证时 支持无限级 如 'user.tokenObj.value' (0.7版本开始支持无限级 之前只支持2级) 50 | 51 | 2. ValidateParam里的express字段使用,一般情况下 可以为空, 52 | 当value是Custem时,express=validateBeanName, 有的验证方法需要多个值 逗号分隔即可。 53 | 反正注意的是 此express字段和验证的value方法息息相关. 54 | 55 | 3. 用到了JDK8的新特性,因此JDK版本需要大于等于 **1.8** 56 | 57 | 58 | if illegal paramas then will throw ParamsValidException, and you should catch this special exception 59 | and resolve it. 60 | 61 | # 更新记录 62 | 更新记录,请查看changelog.md文件 63 | 64 | **_JDK>=1.8_** 65 | 66 | # 快速开始(quick start) 67 | 68 | ## web 69 | ``` 70 | package com.github.liangbaika.validate.test; 71 | 72 | import com.github.liangbaika.validate.annations.ValidateParam; 73 | import com.github.liangbaika.validate.annations.ValidateParams; 74 | import com.github.liangbaika.validate.enums.Check; 75 | import org.springframework.web.bind.annotation.*; 76 | 77 | import javax.validation.Valid; 78 | 79 | /** 80 | * @author liangbaikai 81 | * @version 1.0 82 | * @date 2020/6/16 11:56 83 | */ 84 | @RestController 85 | @RequestMapping("/test") 86 | public class TestController { 87 | 88 | 89 | /** 90 | * 单字段简单验证 91 | * 92 | * @param name 93 | * @return 94 | */ 95 | @GetMapping("test0") 96 | @ValidateParam(value = Check.Length, argName = "name", express = "2,6",msg="自定义提示信息") 97 | public Object test0(String name) { 98 | return Boolean.TRUE; 99 | } 100 | 101 | /** 102 | * 多字段验证 103 | * 建议 如果字段过多(超过三个)使用自定义方式 更简洁 104 | * @param name 105 | * @param age 106 | * @param idcard 107 | * @return 108 | */ 109 | @ValidateParams( 110 | value = { 111 | @ValidateParam(value = Check.NotEmpty, argName = "name"), 112 | @ValidateParam(value = Check.Number, argName = "age"), 113 | @ValidateParam(value = Check.isIDCard, argName = "idcard"), 114 | } 115 | ) 116 | @GetMapping("test1") 117 | public Object test1(String name, Integer age, Integer idcard) { 118 | return Boolean.TRUE; 119 | } 120 | 121 | /** 122 | * 实体验证 javax-validation验证方式 123 | * 124 | * @param oneData 125 | * @return 126 | */ 127 | @PostMapping("test2") 128 | public Object test2(@RequestBody @Valid OneData oneData) { 129 | return oneData; 130 | } 131 | 132 | 133 | /** 134 | * 对象多级验证,混合验证 135 | * 0.7 版本开始支持无限级 0.7版本之前的只支持2级 136 | * 0.8.8开始支持重复注解 137 | * @param oneData 138 | * @return 139 | */ 140 | @PostMapping("test3") 141 | @ValidateParam(value = Check.NotEmpty, argName = "oneData.name") 142 | @ValidateParam(value = Check.Number, argName = "oneData.age") 143 | public Object test3(@RequestBody @Valid OneData oneData) { 144 | return oneData; 145 | } 146 | 147 | 148 | /** 149 | * 自定义验证 150 | * 151 | * @param oneData 152 | * @return 153 | */ 154 | @PostMapping("test4") 155 | @ValidateParam(value = Check.Custom, argName = "oneData.name", express = "nameValidater") 156 | public Object test4(@RequestBody OneData oneData) { 157 | return oneData; 158 | } 159 | 160 | @GetMapping("/test6") 161 | @ValidateParam(required = false, value = Check.NotNull, argName = "age") 162 | public RestResponse test6(String name, Integer age) { 163 | return RestResponse.success(); 164 | } 165 | 166 | @GetMapping("/test8") 167 | @ValidateParams( 168 | value = { 169 | @ValidateParam(value = Check.NotNull, argName = "name"), 170 | @ValidateParam(required = false, value = Check.NotNull, argName = "age") 171 | }, 172 | anded = false 173 | ) 174 | public RestResponse test8(String name, Integer age) { 175 | return RestResponse.success(); 176 | } 177 | } 178 | 179 | 180 | package com.github.liangbaika.validate.test; 181 | 182 | import com.github.liangbaika.validate.annations.AbcValidate; 183 | import com.github.liangbaika.validate.enums.Check; 184 | 185 | /** 186 | * @author liangbaikai 187 | * @version 1.0 188 | * @date 2020/6/16 11:59 189 | */ 190 | 191 | public class OneData { 192 | 193 | //自定义的时候 fun=Custom;express= bean名字 194 | @AbcValidate(required = true, fun = Check.Custom, express = "nameValidater") 195 | private String name; 196 | 197 | 198 | @AbcValidate(required = true) 199 | private Integer age; 200 | 201 | 202 | @AbcValidate(required = true, fun = Check.le, express = "1") 203 | private Integer sex; 204 | 205 | 206 | public String getName() { 207 | return name; 208 | } 209 | 210 | public void setName(String name) { 211 | this.name = name; 212 | } 213 | 214 | public Integer getAge() { 215 | return age; 216 | } 217 | 218 | public void setAge(Integer age) { 219 | this.age = age; 220 | } 221 | 222 | public Integer getSex() { 223 | return sex; 224 | } 225 | 226 | public void setSex(Integer sex) { 227 | this.sex = sex; 228 | } 229 | } 230 | 231 | 232 | 233 | 234 | 235 | 236 | package com.github.liangbaika.validate.test; 237 | 238 | import com.github.liangbaika.validate.core.ParamValidator; 239 | import org.springframework.stereotype.Component; 240 | 241 | /** 242 | * 自定义验证器 模拟复杂逻辑校验 243 | * 需要被spring容器管理 244 | * @author liangbaikai 245 | * @version 1.0 246 | * @date 2020/6/16 12:01 247 | */ 248 | @Component 249 | public class NameValidater implements ParamValidator { 250 | 251 | // 也可以在这里面抛出 com.github.liangbaika.validate.exception.ParamsInValidException 特定异常 252 | 253 | @Override 254 | public Boolean validate(Object value) { 255 | String name = (String) value; 256 | if (name.startsWith("张") && name.length() == 3) { 257 | return true; 258 | } 259 | return false; 260 | } 261 | 262 | 263 | } 264 | 265 | ``` 266 | 267 | ## 普通代码形式 268 | 269 | 270 | ``` 271 | package com.github.liangbaika.validate.test; 272 | 273 | import com.github.liangbaika.validate.core.ValidateBuilder; 274 | 275 | import static com.github.liangbaika.validate.enums.Check.*; 276 | 277 | /** 278 | * 测试 279 | * 280 | * @author lq 281 | * @version 1.0 282 | * @date 2021/1/29 21:44 283 | */ 284 | public class Tests { 285 | public static void main(String[] args) { 286 | 287 | ValidateBuilder validateBuilder = ValidateBuilder.build(); 288 | int failedCounts = validateBuilder 289 | .vali(ne, "测试不等", "测试不等") 290 | .vali(Chinese, "测试中文") 291 | .vali(isBirthdaystr, "1992-12-09") 292 | .vali(isUrl, "https://baidu.com") 293 | .doCheck() 294 | .getFailedCounts(); 295 | System.out.println(failedCounts); 296 | System.out.println(validateBuilder.isPassed()); 297 | System.out.println(validateBuilder.getSuccedCounts()); 298 | System.out.println(validateBuilder.getFailedMsgs()); 299 | 300 | // 重复使用 validateBuilder 先调用clear方法 301 | int failedCounts2 = validateBuilder 302 | .clear() 303 | .vali(isGeneral, "ssa2_") 304 | .vali(Chinese, "测试中文") 305 | .vali(isBirthdaystr, "1992/12/09") 306 | .vali(isUrl, "http://baidu.com") 307 | .doCheck() 308 | .ifNotPassedThrowException() 309 | .getFailedCounts(); 310 | System.out.println(failedCounts2); 311 | System.out.println(validateBuilder.getSuccedCounts()); 312 | 313 | } 314 | } 315 | 316 | ``` 317 | 318 | # 参数异常错误统一处理( resolve error) 319 | ``` 320 | @ControllerAdvice 321 | public class GlobalExceptionHandler { 322 | 323 | private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class); 324 | 325 | @ExceptionHandler({ParamsInValidException.class, MethodArgumentNotValidException.class, BindException.class}) 326 | @ResponseBody 327 | public RestResponse handleParamsValidException(Exception e) { 328 | if (e instanceof ParamsInValidException) { 329 | return RestResponse.error(new ErrorCode("500001", e.getMessage())); 330 | } else { 331 | BindingResult bindingResult = null; 332 | if (e instanceof BindException) { 333 | bindingResult = ((BindException) e).getBindingResult(); 334 | } else { 335 | bindingResult = ((MethodArgumentNotValidException) e).getBindingResult(); 336 | } 337 | StringBuilder errMsg = getErrorInfo(bindingResult); 338 | return RestResponse.error(new ErrorCode("500001", errMsg.toString())); 339 | } 340 | } 341 | 342 | 343 | private StringBuilder getErrorInfo(BindingResult bindingResult) { 344 | List allErrors = bindingResult.getAllErrors(); 345 | StringBuilder errMsg = new StringBuilder(); 346 | for (ObjectError allError : allErrors) { 347 | FieldError fieldError = (FieldError) allError; 348 | String field = fieldError.getField(); 349 | String defaultMessage = fieldError.getDefaultMessage(); 350 | errMsg.append(field + ":" + defaultMessage + "; "); 351 | } 352 | return errMsg; 353 | } 354 | 355 | 356 | } 357 | 358 | ``` 359 | 360 | 361 | # 欢迎使用 (Thanks and Suggestions) 362 | 此项目采用 Apache License协议, 欢迎大家使用或提出建议或贡献代码 363 | 如果需要PR,请遵守标准提PR的那套标准原则。 364 | You are welcome to use or suggest or contribute code 365 | If PR is required, please follow the set of standard principles mentioned in the standard. 366 | -------------------------------------------------------------------------------- /changelog.md: -------------------------------------------------------------------------------- 1 | # 更新日志 2 | 3 | 4 | ## 版本 0.9.4 5 | 1. 修复兼容性问题 6 | 2. 更新demo里的错误处理方式 7 | 3. 代码规范插件扫描并修复优化 8 | 9 | ## 版本 0.9.5 10 | 1. 优化代码 11 | 2. 修复Equal默认验证的错误提示 12 | 3. Check.Equal 标记过时 新增 Check.eq 13 | 14 | ## 版本 0.9.6 15 | 1. 修复以对象.属性(Obj.xxx.xx)验证时 属性不存在时 让错误提示更明显 详细 16 | 2. 修复以对象.属性(Obj.xxx.xx)验证时 限制最大递归深度 17 | 3. 完善注释 18 | 19 | ## 版本 0.9.7 20 | 1. ParamValidator 改为泛型支持 21 | 2. 删除 Check.Equal 请使用Check.eq方法 22 | 23 | ## 版本 0.9.7.1 24 | 1. 修复issue#2 错误提示消息粘连问题 25 | 2. 根据规约修复部分warning代码 26 | 27 | 28 | ## 版本 1.0.0 29 | 1. 修复bool类型值无法找到getter方法的问题 30 | 2. 稳定大约半年了, 1.0.0版本正式 realeased !!! 31 | 32 | ## 版本 1.1.0 33 | 1. fix issue4 34 | 2. 新增ValidateBuilder 普通代码形式验证 以支持多场景下的使用 35 | 36 | ## 版本 1.2.0 37 | 1. 修改部分ValidateBuilder api 38 | 2. 新增测试 修改部分提示 新增普通代码使用demo, 修改部分文档记录 优化ValidateBuilder代码 39 | 40 | ## 版本 1.2.2 41 | 1. 修复@AbcValidate 注解在require=false 但是在值不为空的时候 跳过校验函数的问题 42 | 43 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.springframework.boot 7 | spring-boot-starter-parent 8 | 2.0.4.RELEASE 9 | 10 | 11 | com.github.liangbaika 12 | validate-spring-boot-starter 13 | 1.2.2 14 | validate 15 | validation tool with springboot 16 | 17 | 18 | 1.8 19 | 20 | 21 | 22 | 23 | 24 | 25 | org.apache.maven.plugins 26 | maven-javadoc-plugin 27 | 2.7 28 | 29 | 30 | attach-javadoc 31 | 32 | jar 33 | 34 | 35 | 36 | 37 | public 38 | UTF-8 39 | UTF-8 40 | UTF-8 41 | -Xdoclint:none 42 | 43 | 44 | 45 | org.apache.maven.plugins 46 | maven-source-plugin 47 | 2.2.1 48 | 49 | 50 | attach-sources 51 | 52 | jar-no-fork 53 | 54 | 55 | 56 | 57 | 58 | org.apache.maven.plugins 59 | maven-compiler-plugin 60 | 3.7.0 61 | 62 | ${java.version} 63 | ${java.version} 64 | UTF-8 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | The Apache Software License, Version 2.0 74 | http://www.apache.org/licenses/LICENSE-2.0.txt 75 | 76 | 77 | 78 | 79 | liangbaikai 80 | 1144388620@qq.com 81 | 82 | 83 | 84 | scm:git:https://github.com/liangbaika/validate.git 85 | scm:git:https://github.com/liangbaika/validate.git 86 | https://github.com/liangbaika/validate.git 87 | 88 | 89 | 90 | 91 | 92 | release 93 | 94 | 95 | sonatype-nexus-staging 96 | https://oss.sonatype.org/service/local/staging/deploy/maven2 97 | 98 | 99 | sonatype-nexus-snapshots 100 | https://oss.sonatype.org/content/repositories/snapshots 101 | 102 | 103 | 104 | 105 | 106 | org.apache.maven.plugins 107 | maven-gpg-plugin 108 | 1.5 109 | 110 | 111 | sign-artifacts 112 | verify 113 | 114 | sign 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | org.springframework.boot 128 | spring-boot-starter-aop 129 | provided 130 | 131 | 132 | org.springframework.boot 133 | spring-boot-starter-web 134 | provided 135 | 136 | 137 | javax.validation 138 | validation-api 139 | 2.0.1.Final 140 | 141 | 142 | junit 143 | junit 144 | 4.13.1 145 | test 146 | 147 | 148 | 149 | 150 | -------------------------------------------------------------------------------- /src/main/java/com/github/liangbaika/validate/annations/AbcValidate.java: -------------------------------------------------------------------------------- 1 | package com.github.liangbaika.validate.annations; 2 | 3 | import com.github.liangbaika.validate.core.AbcValidator; 4 | import com.github.liangbaika.validate.enums.Check; 5 | import com.github.liangbaika.validate.exception.ParamsInValidException; 6 | 7 | import javax.validation.Constraint; 8 | import javax.validation.Payload; 9 | import java.lang.annotation.*; 10 | 11 | import static java.lang.annotation.ElementType.*; 12 | import static java.lang.annotation.RetentionPolicy.RUNTIME; 13 | 14 | /** 15 | * 注解 一般用于bean字段 方法上做参数校验 实现了JSR303规范。 16 | * 17 | * @author liangbaikai 18 | * @version 0.1.0 19 | * @date 2020/5/15 18:15 20 | * @see ParamsInValidException 21 | */ 22 | @Target({METHOD, FIELD, CONSTRUCTOR, PARAMETER, ANNOTATION_TYPE}) 23 | @Retention(RUNTIME) 24 | @Documented 25 | @Constraint(validatedBy = {AbcValidator.class}) 26 | @Repeatable(AbcValidate.List.class) 27 | public @interface AbcValidate { 28 | 29 | /** 30 | * 是否必传 31 | */ 32 | boolean required() default false; 33 | 34 | /** 35 | * 实际上会取验证函数的msg 自定义此值的话优先级会覆盖的 36 | */ 37 | String message() default "参数验证错误"; 38 | 39 | /** 40 | * 分组 41 | */ 42 | Class[] groups() default {}; 43 | 44 | /** 45 | * 极少用到 携带数据 46 | */ 47 | Class[] payload() default {}; 48 | 49 | /** 50 | * 验证函数 51 | * 52 | * @return 53 | */ 54 | Check fun() default Check.NotNull; 55 | 56 | /** 57 | * 多个值逗号隔开 此值和fun的里的验证方法息息相关 58 | */ 59 | String express() default ""; 60 | 61 | /** 62 | * Defines several {@link AbcValidate} annotations on the same element. 63 | * 64 | * @see AbcValidate 65 | */ 66 | @Target({METHOD, FIELD, CONSTRUCTOR, PARAMETER, ANNOTATION_TYPE}) 67 | @Retention(RUNTIME) 68 | @Documented 69 | @interface List { 70 | AbcValidate[] value(); 71 | } 72 | 73 | 74 | } 75 | -------------------------------------------------------------------------------- /src/main/java/com/github/liangbaika/validate/annations/ValidateParam.java: -------------------------------------------------------------------------------- 1 | package com.github.liangbaika.validate.annations; 2 | 3 | /** 4 | * 验证注解 一般放在 Controoler里的路由方法上使用 5 | * 如果字段过多 建议使用自定义方式 6 | * 7 | * @author liangbaikai 8 | * @version 0.1.0 9 | * @date 2020/5/15 18:15 10 | */ 11 | 12 | 13 | import com.github.liangbaika.validate.enums.Check; 14 | 15 | import java.lang.annotation.*; 16 | 17 | 18 | @Target(ElementType.METHOD) 19 | @Retention(RetentionPolicy.RUNTIME) 20 | @Documented 21 | @Repeatable(ValidateParam.List.class) 22 | public @interface ValidateParam { 23 | 24 | 25 | /** 26 | * 是否必传; JSR303里默认false,此处由于历史兼容原因默认true; 需要注意 27 | * 28 | * @return 29 | */ 30 | boolean required() default true; 31 | 32 | 33 | /** 34 | * 函数 参数校验调用方法 35 | * 36 | * @return 37 | */ 38 | Check value() default Check.NotNull; 39 | 40 | /** 41 | * 多个值逗号隔开 此值和value的里的验证方法息息相关 42 | * 43 | * @return 44 | */ 45 | String express() default ""; 46 | 47 | /** 48 | * 参数名称用 ; .表示层级,支持无限级如: entityObj.userObj.age 49 | * 50 | * @return 51 | */ 52 | String argName(); 53 | 54 | 55 | /** 56 | * 自定义错误提示信息 特殊的才需要填 一般不用填 57 | * 58 | * @return 59 | */ 60 | String msg() default ""; 61 | 62 | 63 | /** 64 | * Defines several {@link ValidateParam} annotations on the same element. 65 | * 66 | * @see ValidateParam 67 | */ 68 | @Target(ElementType.METHOD) 69 | @Retention(RetentionPolicy.RUNTIME) 70 | @Documented 71 | @interface List { 72 | ValidateParam[] value(); 73 | } 74 | 75 | 76 | } -------------------------------------------------------------------------------- /src/main/java/com/github/liangbaika/validate/annations/ValidateParams.java: -------------------------------------------------------------------------------- 1 | package com.github.liangbaika.validate.annations; 2 | 3 | import java.lang.annotation.*; 4 | 5 | /** 6 | * 验证注解 一般放在 Controoler里的路由方法上使用 7 | * 8 | * @author liangbaikai 9 | * @version 0.1.0 10 | * @date 2020/5/15 18:16 11 | */ 12 | @Target(ElementType.METHOD) 13 | @Retention(RetentionPolicy.RUNTIME) 14 | public @interface ValidateParams { 15 | 16 | /** 17 | * 多个CheckParam,由上往下判断 18 | * 19 | * @return 20 | */ 21 | ValidateParam[] value(); 22 | 23 | /** 24 | * true 多个验证时 只要有一个不通过就立即返回错误 并停止验证,效率稍微高点,尤其某些验证比较耗时,比如需要查库的 25 | * false 一次验证所有的,返回完整的错误信息,可更快看到完整错误信息 26 | * 何时使用 取决于你 27 | * 28 | * @return 29 | */ 30 | boolean shortPath() default false; 31 | 32 | /** 33 | * true 多个条件 与(and); 34 | * false 多个条件 或(or); 35 | * value.length<=1 时 无效; 36 | * 37 | * @return 38 | */ 39 | boolean anded() default true; 40 | 41 | 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/com/github/liangbaika/validate/config/SpringValidateAutoConfig.java: -------------------------------------------------------------------------------- 1 | package com.github.liangbaika.validate.config; 2 | 3 | import com.github.liangbaika.validate.core.CheckParamAop; 4 | import com.github.liangbaika.validate.utils.SpringContextHolder; 5 | import org.springframework.context.annotation.Configuration; 6 | import org.springframework.context.annotation.Import; 7 | 8 | /** 9 | * springboot stater 自动装配 10 | * 11 | * @author liangbaikai 12 | * @version 0.1.0 13 | * @date 2020/5/17 14:23 14 | */ 15 | @Configuration 16 | @Import({CheckParamAop.class, SpringContextHolder.class}) 17 | public class SpringValidateAutoConfig { 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/com/github/liangbaika/validate/core/AbcValidator.java: -------------------------------------------------------------------------------- 1 | package com.github.liangbaika.validate.core; 2 | 3 | import com.github.liangbaika.validate.annations.AbcValidate; 4 | import com.github.liangbaika.validate.enums.Check; 5 | 6 | import javax.validation.ConstraintValidator; 7 | import javax.validation.ConstraintValidatorContext; 8 | 9 | /** 10 | * 自定义验证注解支持类 jsr303 11 | * 12 | * @author liangbaikai 13 | * @version 0.1.0 14 | * @date 2020/5/15 18:14 15 | */ 16 | public class AbcValidator implements ConstraintValidator { 17 | 18 | private static final String DEFAULT_MSG = "参数验证错误"; 19 | 20 | 21 | private boolean required = false; 22 | private Check func; 23 | private String express; 24 | private String msg; 25 | 26 | 27 | public AbcValidator() { 28 | } 29 | 30 | /** 31 | * jsr303 初始化 32 | * 33 | * @param constraintAnnotation 34 | */ 35 | @Override 36 | public void initialize(AbcValidate constraintAnnotation) { 37 | required = constraintAnnotation.required(); 38 | func = constraintAnnotation.fun(); 39 | express = constraintAnnotation.express(); 40 | msg = constraintAnnotation.message(); 41 | if (DEFAULT_MSG.equals(msg)) { 42 | msg = func.msg + express; 43 | } 44 | } 45 | 46 | 47 | /** 48 | * jsr303 验证 49 | * 这里面尽量不要抛出异常 50 | * 51 | * @param value 52 | * @param context 53 | * @return 54 | */ 55 | @Override 56 | public boolean isValid(Object value, ConstraintValidatorContext context) { 57 | String tmpMsg = msg; 58 | Boolean res = false; 59 | if (value == null && !required) { 60 | return true; 61 | } 62 | try { 63 | res = func.fun.apply(value, express); 64 | } catch (Exception e) { 65 | // handle exception 66 | String errorMessage = ""; 67 | if (e.getCause() != null && e.getCause().getMessage() != null) { 68 | errorMessage = e.getCause().getMessage(); 69 | } else { 70 | errorMessage = e.getMessage(); 71 | } 72 | tmpMsg = msg + "; raw exception occured, info: " + errorMessage; 73 | } 74 | if (!res) { 75 | context.disableDefaultConstraintViolation(); 76 | context.buildConstraintViolationWithTemplate(tmpMsg) 77 | .addConstraintViolation(); 78 | } 79 | return res; 80 | 81 | } 82 | 83 | } -------------------------------------------------------------------------------- /src/main/java/com/github/liangbaika/validate/core/CheckParamAop.java: -------------------------------------------------------------------------------- 1 | package com.github.liangbaika.validate.core; 2 | 3 | 4 | import com.github.liangbaika.validate.annations.ValidateParam; 5 | import com.github.liangbaika.validate.annations.ValidateParams; 6 | import com.github.liangbaika.validate.exception.ParamsCheckException; 7 | import com.github.liangbaika.validate.exception.ParamsInValidException; 8 | import org.aspectj.lang.JoinPoint; 9 | import org.aspectj.lang.ProceedingJoinPoint; 10 | import org.aspectj.lang.Signature; 11 | import org.aspectj.lang.annotation.Around; 12 | import org.aspectj.lang.annotation.Aspect; 13 | import org.aspectj.lang.annotation.Pointcut; 14 | import org.aspectj.lang.reflect.MethodSignature; 15 | import org.springframework.stereotype.Component; 16 | 17 | import java.lang.reflect.InvocationTargetException; 18 | import java.lang.reflect.Method; 19 | import java.util.ArrayList; 20 | import java.util.Arrays; 21 | import java.util.List; 22 | import java.util.stream.Collectors; 23 | 24 | /** 25 | * AOP切面验证 26 | * 相比 hibernate-validate相比 此方法更加灵活多样。 27 | * 也是验证的核心逻辑 28 | * 29 | * @author liangbaikai 30 | * @version 0.1.0 31 | * @date 2020/5/15 18:14 32 | */ 33 | @Aspect 34 | @Component 35 | public class CheckParamAop { 36 | 37 | @Pointcut("@annotation(com.github.liangbaika.validate.annations.ValidateParam)") 38 | public void checkParam() { 39 | } 40 | 41 | @Pointcut("@annotation(com.github.liangbaika.validate.annations.ValidateParam.List)") 42 | public void checkParamList() { 43 | } 44 | 45 | @Pointcut("@annotation(com.github.liangbaika.validate.annations.ValidateParams)") 46 | public void checkParams() { 47 | } 48 | 49 | 50 | @Around("checkParam()") 51 | public Object checkSingle(ProceedingJoinPoint point) throws Throwable { 52 | return interCheck(point, false, false); 53 | } 54 | 55 | 56 | @Around("checkParams()") 57 | public Object checkGroup(ProceedingJoinPoint point) throws Throwable { 58 | return interCheck(point, true, false); 59 | } 60 | 61 | /** 62 | * 拦截JDK编译后的重复注解 63 | * 64 | * @param point 65 | * @return 66 | * @throws Throwable 67 | */ 68 | @Around("checkParamList()") 69 | public Object checkList(ProceedingJoinPoint point) throws Throwable { 70 | return interCheck(point, true, true); 71 | } 72 | 73 | /** 74 | * 校验 75 | * 76 | * @param point 切点 77 | * @param muiti 多个 78 | * @param special 是否JDK编译的重复注解 79 | * @return 80 | * @throws Throwable 81 | */ 82 | private Object interCheck(ProceedingJoinPoint point, boolean muiti, boolean special) throws Throwable { 83 | Object obj; 84 | // 参数校验 85 | SampleResult sampleResult = doCheck(point, muiti, special); 86 | if (!sampleResult.getPass()) { 87 | throw new ParamsInValidException(sampleResult.getMsg()); 88 | } 89 | // 通过校验,继续执行原有方法 90 | obj = point.proceed(); 91 | return obj; 92 | } 93 | 94 | /** 95 | * 参数校验 核心逻辑 96 | * 97 | * @param point 切点 98 | * @param multi 多参数校验 99 | * @return 错误信息 100 | */ 101 | private SampleResult doCheck(JoinPoint point, boolean multi, boolean special) { 102 | Method method = this.getMethod(point); 103 | String[] paramName = this.getParamName(point); 104 | // 获取接口传递的所有参数 105 | Object[] arguments = point.getArgs(); 106 | 107 | Boolean isValid = true; 108 | String msg = " "; 109 | if (multi) { 110 | List list = new ArrayList<>(); 111 | List annoModels = null; 112 | //多个参数校验 AOP监听带注解的方法,所以不用判断注解是否为空 113 | ValidateParams annotation = null; 114 | if (special) { 115 | // javac 编译后的注解 查看class文件可知 自动生成此类型(jdk8新特性) 116 | ValidateParam.List annotationList = method.getAnnotation(ValidateParam.List.class); 117 | annoModels = Arrays.stream(annotationList.value()).map(e -> new AnnoModel(true, e) 118 | ).collect(Collectors.toList()); 119 | } else { 120 | annotation = method.getAnnotation(ValidateParams.class); 121 | annoModels = Arrays.stream(annotation.value()).map(e -> new AnnoModel(false, e) 122 | ).collect(Collectors.toList()); 123 | } 124 | assert annoModels.size() > 0; 125 | list.addAll(annoModels); 126 | 127 | boolean shortPath = false; 128 | boolean anded = true; 129 | if (annotation != null) { 130 | shortPath = annotation.shortPath(); 131 | anded = annotation.anded(); 132 | } 133 | int passCounter = 0; 134 | for (AnnoModel annoModel : list) { 135 | ValidateParam anno = annoModel.getValidateParam(); 136 | boolean required = anno.required(); 137 | String argName = anno.argName(); 138 | //参数值 139 | Object value = this.getParamValue(arguments, paramName, argName); 140 | if (!required && value == null) { 141 | continue; 142 | } 143 | 144 | //调用枚举类的 CheckUtil类方法 145 | Boolean tmpValid = anno.value().fun.apply(value, anno.express()); 146 | if (tmpValid && !annoModel.getSed()) { 147 | passCounter++; 148 | } 149 | if (isValid) { 150 | isValid = tmpValid; 151 | } 152 | // 执行判断 153 | if (!tmpValid) { 154 | // 只要有一个参数判断不通过,立即返回 155 | String tmpMsg = anno.msg(); 156 | msg = msg + " " + tmpMsg; 157 | if ("".equals(tmpMsg)) { 158 | msg += (" " + argName + ": " + anno.value().msg + " " + anno.express() + " ; "); 159 | } 160 | if (shortPath) { 161 | break; 162 | } 163 | } 164 | } 165 | 166 | if (!special && !anded && passCounter > 0) { 167 | msg += " 条件[或] "; 168 | } 169 | 170 | // 或条件成立 171 | if (!anded && list.size() > 1 && passCounter > 0) { 172 | isValid = true; 173 | } 174 | 175 | } else { 176 | // 单个参数校验 177 | // AOP监听带注解的方法,所以不用判断注解是否为空 178 | ValidateParam anno = method.getAnnotation(ValidateParam.class); 179 | boolean required = anno.required(); 180 | String argName = anno.argName(); 181 | //参数值 182 | Object value = this.getParamValue(arguments, paramName, argName); 183 | if (required || value != null) { 184 | // 执行判断 // 调用枚举类的 CheckUtil类方法 185 | isValid = anno.value().fun.apply(value, anno.express()); 186 | msg = anno.msg(); 187 | if ("".equals(msg)) { 188 | msg = argName + ": " + anno.value().msg + " " + anno.express(); 189 | } 190 | } 191 | } 192 | return new SampleResult(msg, isValid); 193 | 194 | } 195 | 196 | 197 | /** 198 | * 获取参数名称 199 | * jdk 1.8 特性 200 | * 201 | * @param joinPoint 202 | * @return 203 | */ 204 | private String[] getParamName(JoinPoint joinPoint) { 205 | Signature signature = joinPoint.getSignature(); 206 | MethodSignature methodSignature = (MethodSignature) signature; 207 | return methodSignature.getParameterNames(); 208 | } 209 | 210 | 211 | /** 212 | * 根据参数名称,获取参数值 213 | * 214 | * @param arguments 215 | * @param paramName 216 | * @param argName 217 | * @return 218 | */ 219 | private Object getParamValue(Object[] arguments, String[] paramName, String argName) { 220 | Object value = null; 221 | String name = argName; 222 | String spliter = "."; 223 | if (argName.contains(spliter)) { 224 | name = argName.split("\\.")[0]; 225 | } 226 | int index = 0; 227 | for (String string : paramName) { 228 | if (string.equals(name)) { 229 | //基本类型取值 // 不做空判断,如果注解配置的参数名称不存在,则取值为null 230 | value = arguments[index]; 231 | break; 232 | } 233 | index++; 234 | } 235 | //从对象中取值 236 | if (argName.contains(spliter)) { 237 | //从实体对象中取值 理论上支持无限级 如 user.tokenObj.value 238 | String[] argNames = argName.split("\\."); 239 | try { 240 | // 最大10级 递归深度 一般情况下来说最大三,四级. 241 | if (argNames != null && argNames.length > 10) { 242 | throw new ParamsCheckException(String.format(" occured validated raw error argName {%s} the recursion is too deep,over 10.. ", 243 | argName)); 244 | } 245 | value = getObjValue(1, value, argNames); 246 | 247 | } catch (NoSuchMethodException e) { 248 | throw new ParamsCheckException(String.format(" occured validated raw error NoSuchMethodException argName {%s} can not found getter method e is {%s}", 249 | argName, e.getMessage())); 250 | } catch (InvocationTargetException e) { 251 | throw new ParamsCheckException(String.format("occured validated raw error InvocationTargetException argName {%s} invoke getter method error e is {%s}", 252 | argName, e.getMessage())); 253 | } catch (IllegalAccessException e) { 254 | throw new ParamsCheckException(String.format(" validated raw error argName {%s} when" + 255 | " get filed value IllegalAccessException occured e is {%s}", argName, e.getMessage())); 256 | } 257 | } 258 | return value; 259 | } 260 | 261 | 262 | /** 263 | * 获取方法 264 | * 265 | * @param joinPoint ProceedingJoinPoint 266 | * @return 方法 267 | */ 268 | private Method getMethod(JoinPoint joinPoint) { 269 | MethodSignature signature = (MethodSignature) joinPoint.getSignature(); 270 | Method method = signature.getMethod(); 271 | if (method.getDeclaringClass().isInterface()) { 272 | try { 273 | method = joinPoint 274 | .getTarget() 275 | .getClass() 276 | .getDeclaredMethod(joinPoint.getSignature().getName(), 277 | method.getParameterTypes()); 278 | } catch (SecurityException | NoSuchMethodException e) { 279 | } 280 | } 281 | return method; 282 | } 283 | 284 | /** 285 | * 对应字段必须要有对应的getter方法 286 | * 反射获取值 无限级 287 | * 288 | * @param index 参数索引 应该从1开始 289 | * @param value 对象 290 | * @param argNames 字段名数组 291 | * @return 292 | * @throws NoSuchMethodException 293 | * @throws InvocationTargetException 294 | * @throws IllegalAccessException 295 | */ 296 | private static Object getObjValue(int index, Object value, String[] argNames) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { 297 | 298 | assert index > 0; 299 | assert argNames != null; 300 | if (index >= argNames.length || value == null) { 301 | return value; 302 | } 303 | 304 | String tempName = argNames[index]; 305 | // 注意 调用公共的getter方法; 306 | String suffix = tempName.substring(0, 1).toUpperCase() + tempName.substring(1); 307 | String filedMethodName = "get" + suffix; 308 | boolean has = hasThisMethodByName(value, filedMethodName); 309 | //某些bool值命名不规范时 可能会找不到对应方法,导致失败。 310 | if (!has) { 311 | filedMethodName = "is" + suffix; 312 | } 313 | Method getterMethod = value.getClass().getMethod(filedMethodName); 314 | Object tempValue = getterMethod.invoke(value); 315 | 316 | return getObjValue(index + 1, tempValue, argNames); 317 | } 318 | 319 | private static boolean hasThisMethodByName(Object obj, String filedMethodName) { 320 | if (obj == null || filedMethodName == null) { 321 | return false; 322 | } 323 | 324 | Method[] methods = obj.getClass().getMethods(); 325 | if (methods == null || methods.length <= 0) { 326 | return false; 327 | } 328 | for (Method method : methods) { 329 | if (filedMethodName.equals(method.getName())) { 330 | return true; 331 | } 332 | } 333 | return false; 334 | } 335 | 336 | /** 337 | * 判断非法参数的简单结果对象 338 | */ 339 | private static class SampleResult { 340 | private String msg; 341 | private Boolean pass; 342 | 343 | 344 | public SampleResult(String msg, Boolean pass) { 345 | this.msg = msg; 346 | this.pass = pass; 347 | } 348 | 349 | public String getMsg() { 350 | return msg; 351 | } 352 | 353 | 354 | public Boolean getPass() { 355 | return pass; 356 | } 357 | 358 | } 359 | 360 | /** 361 | * just a holder 362 | */ 363 | private static class AnnoModel { 364 | 365 | private Boolean sed; 366 | private ValidateParam validateParam; 367 | 368 | public AnnoModel(boolean sed, ValidateParam validateParam) { 369 | this.sed = sed; 370 | this.validateParam = validateParam; 371 | } 372 | 373 | public Boolean getSed() { 374 | return sed; 375 | } 376 | 377 | public ValidateParam getValidateParam() { 378 | return validateParam; 379 | } 380 | 381 | } 382 | 383 | } 384 | -------------------------------------------------------------------------------- /src/main/java/com/github/liangbaika/validate/core/ParamValidator.java: -------------------------------------------------------------------------------- 1 | package com.github.liangbaika.validate.core; 2 | 3 | /** 4 | * 自定义验证器需要实现此接口,并被spring容器托管 5 | * change: 0.9.7版本开始改为泛型支持 6 | * 7 | * @author liangbaikai 8 | * @version 0.1.0 9 | * @date 2020/5/27 13:42 10 | */ 11 | @FunctionalInterface 12 | public interface ParamValidator { 13 | /** 14 | * 自定义实现方法 需要用户自己实现 并让此实现类被spring 容器管理 15 | * 16 | * @param value 值 17 | * @return 18 | */ 19 | Boolean validate(T value); 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/com/github/liangbaika/validate/core/ValidateBuilder.java: -------------------------------------------------------------------------------- 1 | package com.github.liangbaika.validate.core; 2 | 3 | import com.github.liangbaika.validate.enums.Check; 4 | import com.github.liangbaika.validate.exception.ParamsCheckException; 5 | import com.github.liangbaika.validate.exception.ParamsInValidException; 6 | 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | import java.util.Objects; 10 | import java.util.stream.Collectors; 11 | 12 | /** 13 | * 另外一种代码的方式检查参数 14 | * since: 1.1.0 版本 15 | * 16 | * @author liangbaikai 17 | * @version 1.1.0 18 | * @date 2021/1/28 13:42 19 | */ 20 | public class ValidateBuilder { 21 | 22 | /** 23 | * 请使用build方法构造 24 | */ 25 | private ValidateBuilder() { 26 | } 27 | 28 | /** 29 | * 验证相关信息的容器保存类 30 | */ 31 | private List chains = new ArrayList(); 32 | 33 | /** 34 | * 实体类 35 | */ 36 | public static class ValidateChain { 37 | 38 | private Check check; 39 | private Object value; 40 | private String express; 41 | private Boolean result; 42 | private String msg; 43 | 44 | public Check getCheck() { 45 | return check; 46 | } 47 | 48 | public void setCheck(Check check) { 49 | this.check = check; 50 | } 51 | 52 | public Object getValue() { 53 | return value; 54 | } 55 | 56 | public void setValue(Object value) { 57 | this.value = value; 58 | } 59 | 60 | public String getExpress() { 61 | return express; 62 | } 63 | 64 | public void setExpress(String express) { 65 | this.express = express; 66 | } 67 | 68 | public Boolean getResult() { 69 | return result; 70 | } 71 | 72 | public void setResult(Boolean result) { 73 | this.result = result; 74 | } 75 | 76 | public String getMsg() { 77 | return msg; 78 | } 79 | 80 | public void setMsg(String msg) { 81 | this.msg = msg; 82 | } 83 | } 84 | 85 | /** 86 | * @param check 验证的枚举方法 自定义请使用Custom枚举 87 | * @param value 验证的值 88 | * @param express 表达式 非必填 和枚举方法相关 89 | * @param msg 提示信息 没有默认取枚举方法的提示信息 90 | * @return 91 | */ 92 | public ValidateBuilder vali(Check check, Object value, String express, String msg) { 93 | ValidateChain validateChain = new ValidateChain(); 94 | validateChain.setCheck(check); 95 | validateChain.setValue(value); 96 | validateChain.setExpress(express); 97 | validateChain.setMsg(msg == null || Objects.equals(msg, "") ? check.msg : msg); 98 | chains.add(validateChain); 99 | return this; 100 | } 101 | 102 | public ValidateBuilder vali(Check check, Object value, String msg) { 103 | return vali(check, value, null, msg); 104 | } 105 | 106 | public ValidateBuilder vali(Check check, Object value) { 107 | return vali(check, value, null, null); 108 | } 109 | 110 | public ValidateBuilder wvali(Check check, Object value, String express) { 111 | return vali(check, value, express, null); 112 | } 113 | 114 | /** 115 | * 真正检查的方法 116 | * 117 | * @return ValidateBuilder 118 | */ 119 | public ValidateBuilder doCheck() { 120 | for (ValidateChain conn : chains) { 121 | Boolean result = conn.getCheck().vali(conn.value, conn.express); 122 | conn.setResult(result); 123 | } 124 | return this; 125 | } 126 | 127 | /** 128 | * 此次验证是否通过 129 | * 130 | * @return Boolean 131 | */ 132 | public Boolean isPassed() { 133 | if (chains == null || chains.isEmpty()) { 134 | throw new ParamsCheckException("chains can not be empty, please call methods 'vali' and 'doCheck' first"); 135 | } 136 | List faileds = chains.stream().filter(e -> Boolean.FALSE.equals(e.getResult())).collect(Collectors.toList()); 137 | return faileds.size() <= 0; 138 | } 139 | 140 | /** 141 | * 不通过就抛出异常 142 | * ParamsInValidException 143 | * 144 | * @return ValidateBuilder 145 | */ 146 | public ValidateBuilder ifNotPassedThrowException() { 147 | return ifNotPassedThrowException(null); 148 | } 149 | 150 | /** 151 | * 不通过就抛出异常 152 | * 153 | * @return ValidateBuilder 154 | */ 155 | public ValidateBuilder ifNotPassedThrowException(RuntimeException e) { 156 | Boolean passed = isPassed(); 157 | if (e == null) { 158 | e = new ParamsInValidException(getFailedMsgs()); 159 | } 160 | if (!passed) { 161 | throw e; 162 | } 163 | return this; 164 | } 165 | 166 | /** 167 | * 获取失败的消息 168 | * 没有失败消息返回null 169 | * 170 | * @return String 171 | */ 172 | public String getFailedMsgs() { 173 | if (chains == null || chains.isEmpty()) { 174 | return null; 175 | } 176 | String failedMsgs = chains.stream().filter(e -> Boolean.FALSE.equals(e.getResult())) 177 | .map(e -> e.getValue() + " " + e.getMsg() + " " + (e.getExpress() == null ? "" : e.getExpress())) 178 | .collect(Collectors.joining(",")); 179 | 180 | return "".equals(failedMsgs) ? null : failedMsgs; 181 | } 182 | 183 | /** 184 | * 获取失败的条数 185 | * 186 | * @return int 187 | */ 188 | public int getFailedCounts() { 189 | if (chains == null || chains.isEmpty()) { 190 | return 0; 191 | } 192 | return chains.stream() 193 | .filter(e -> Boolean.FALSE.equals(e.getResult())) 194 | .collect(Collectors.toList()) 195 | .size(); 196 | } 197 | 198 | /** 199 | * 获取成功的条数 200 | * 201 | * @return int 202 | */ 203 | public int getSuccedCounts() { 204 | return chains.size() - getFailedCounts(); 205 | } 206 | 207 | public List getChains() { 208 | return chains; 209 | } 210 | 211 | public static ValidateBuilder build() { 212 | return new ValidateBuilder(); 213 | } 214 | 215 | public ValidateBuilder clear() { 216 | chains.clear(); 217 | return this; 218 | } 219 | 220 | 221 | } 222 | -------------------------------------------------------------------------------- /src/main/java/com/github/liangbaika/validate/enums/Check.java: -------------------------------------------------------------------------------- 1 | package com.github.liangbaika.validate.enums; 2 | 3 | 4 | import com.github.liangbaika.validate.utils.CheckUtil; 5 | 6 | import java.util.function.BiFunction; 7 | 8 | /** 9 | * 验证方法 枚举 10 | * 11 | * @author liangbaikai 12 | * @version 0.1.0 13 | * @date 2020/5/15 18:17 14 | */ 15 | public enum Check { 16 | /** 17 | * 特殊 用于自定义验证器逻辑 此时 express = beanName 18 | */ 19 | Custom("参数验证不通过", CheckUtil::customValidate), 20 | 21 | Null("参数必须为空", CheckUtil::isNull), 22 | 23 | NotNull("参数必须不为空", CheckUtil::isNotNull), 24 | 25 | Empty("参数的必须为空", CheckUtil::isEmpty), 26 | 27 | NotEmpty("参数必须非空", CheckUtil::isNotEmpty), 28 | 29 | True("参数必须为 true", CheckUtil::isTrue), 30 | 31 | False("参数必须为 false", CheckUtil::isFalse), 32 | 33 | Date("参数必须是一个日期 yyyy-MM-dd", CheckUtil::isDate), 34 | 35 | DateTime("参数必须是一个日期时间 yyyy-MM-dd HH:mm:ss", CheckUtil::isDateTime), 36 | 37 | TimeMillSeconds("参数必须是一个时间毫秒值", CheckUtil::isTimeMillSeconds), 38 | 39 | Past("参数必须是一个过去的日期", CheckUtil::isPast), 40 | 41 | Future("参数必须是一个将来的日期", CheckUtil::isFuture), 42 | 43 | Today("参数必须今天的日期", CheckUtil::isToday), 44 | 45 | Enum("参数必须在枚举中", CheckUtil::inEnum), 46 | 47 | Email("参数必须是Email地址", CheckUtil::isEmail), 48 | 49 | MobilePhone("参数必须是手机号", CheckUtil::isMobilePhone), 50 | 51 | Number("参数必须是数字类型", CheckUtil::isNumber), 52 | 53 | Range("参数必须在合适的范围内", CheckUtil::inRange), 54 | 55 | NotIn("参数必须不在指定的范围内", CheckUtil::outRange), 56 | 57 | Length("参数长度必须在指定范围内", CheckUtil::inLength), 58 | 59 | gt("参数必须大于指定值", CheckUtil::isGreaterThan), 60 | 61 | lt("参数必须小于指定值", CheckUtil::isLessThan), 62 | 63 | ge("参数必须大于等于指定值", CheckUtil::isGreaterThanEqual), 64 | 65 | le("参数必须小于等于指定值", CheckUtil::isLessThanEqual), 66 | 67 | ne("参数必须不等于指定值", CheckUtil::isNotEqual), 68 | 69 | eq("参数必须等于指定值", CheckUtil::isEqual), 70 | 71 | Pattern("参数必须符合指定的正则表达式", CheckUtil::isPattern), 72 | 73 | Chinese("参数必须是汉字", CheckUtil::isChinese), 74 | 75 | isUrl("参数必须是一个完整的url", CheckUtil::isUrl), 76 | 77 | isISBN("参数必须是一个书籍ISBN编号", CheckUtil::isISBN), 78 | 79 | isBankNumber("参数必须是一个银行卡号", CheckUtil::isBankNumber), 80 | 81 | isChinesePostCode("参数必须是中国邮编", CheckUtil::isChinesePostCode), 82 | 83 | isPlateNumber("参数必须是中国车牌号", CheckUtil::isPlateNumber), 84 | 85 | isUUID("参数必须是UUID", CheckUtil::isUUID), 86 | 87 | isIpv4("参数必须是ipv4", CheckUtil::isIpv4), 88 | 89 | isIpv6("参数必须是ipv6", CheckUtil::isIpv6), 90 | 91 | isMac("参数必须是mac地址", CheckUtil::isMac), 92 | 93 | isIDCard("参数必须是身份证", CheckUtil::isIdCard), 94 | 95 | isGeneral("参数必须是英文字母,数字和下划线", CheckUtil::isGeneral), 96 | 97 | isBirthdaystr("参数必须是生日字符串格式", CheckUtil::isBirthday), 98 | 99 | isSuitableFileLength("文件太大啦", CheckUtil::isSuitableFileLength), 100 | 101 | isSuitableFileSuffix("文件必须是有效合法后缀的文件", CheckUtil::isSuitableFileSuffix); 102 | 103 | 104 | /** 105 | * msg 信息 106 | */ 107 | public String msg; 108 | 109 | /** 110 | * BiFunction:接收字段值(Object)和 表达式(String),返回是否符合规则(Boolean) 111 | */ 112 | public BiFunction fun; 113 | 114 | Check(String msg, BiFunction fun) { 115 | this.msg = msg; 116 | this.fun = fun; 117 | } 118 | 119 | public Boolean vali(Object value, String express) { 120 | return this.fun.apply(value, express); 121 | } 122 | 123 | public Boolean vali(Object value) { 124 | return vali(value,null); 125 | } 126 | 127 | 128 | } -------------------------------------------------------------------------------- /src/main/java/com/github/liangbaika/validate/exception/ParamsCheckException.java: -------------------------------------------------------------------------------- 1 | package com.github.liangbaika.validate.exception; 2 | 3 | /** 4 | * 参数校验异常 未知异常 5 | * 6 | * @author liangbaikai 7 | * @version 0.5.0 8 | * @date 2020/5/15 18:14 9 | * @since 0.5.0 10 | */ 11 | public class ParamsCheckException extends ParamsInValidException { 12 | 13 | public ParamsCheckException() { 14 | } 15 | 16 | public ParamsCheckException(String message) { 17 | super(message); 18 | } 19 | 20 | public ParamsCheckException(String message, Throwable cause) { 21 | super(message, cause); 22 | } 23 | 24 | public ParamsCheckException(Throwable cause) { 25 | super(cause); 26 | } 27 | 28 | public ParamsCheckException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { 29 | super(message, cause, enableSuppression, writableStackTrace); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/com/github/liangbaika/validate/exception/ParamsInValidException.java: -------------------------------------------------------------------------------- 1 | package com.github.liangbaika.validate.exception; 2 | 3 | /** 4 | * 参数校验异常 抛出此异常 自行捕获处理 5 | * 已更名 ParamsValidException -> ParamsInValidException 6 | * 7 | * @author liangbaikai 8 | * @version 0.1.0 9 | * @date 2020/5/15 18:14 10 | * @apiNote ParamsValidException -> ParamsInValidException 11 | * @since 0.5.0 12 | */ 13 | public class ParamsInValidException extends RuntimeException { 14 | 15 | public ParamsInValidException() { 16 | } 17 | 18 | public ParamsInValidException(String message) { 19 | super(message); 20 | } 21 | 22 | public ParamsInValidException(String message, Throwable cause) { 23 | super(message, cause); 24 | } 25 | 26 | public ParamsInValidException(Throwable cause) { 27 | super(cause); 28 | } 29 | 30 | public ParamsInValidException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { 31 | super(message, cause, enableSuppression, writableStackTrace); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/com/github/liangbaika/validate/utils/CheckUtil.java: -------------------------------------------------------------------------------- 1 | package com.github.liangbaika.validate.utils; 2 | 3 | 4 | import com.github.liangbaika.validate.core.ParamValidator; 5 | import com.github.liangbaika.validate.exception.ParamsCheckException; 6 | import org.springframework.web.multipart.MultipartFile; 7 | 8 | import java.io.File; 9 | import java.math.BigDecimal; 10 | import java.net.MalformedURLException; 11 | import java.time.LocalDate; 12 | import java.time.LocalDateTime; 13 | import java.time.format.DateTimeFormatter; 14 | import java.util.*; 15 | import java.util.function.Function; 16 | import java.util.regex.Matcher; 17 | import java.util.regex.Pattern; 18 | 19 | import static com.github.liangbaika.validate.utils.CheckUtil.RegexPattern.*; 20 | 21 | 22 | /** 23 | * 验证方法 24 | * 25 | * @author liangbaiakai 26 | * @version 0.1.0 27 | * @date 2020/5/15 18:17 28 | */ 29 | public class CheckUtil { 30 | 31 | 32 | /** 33 | * 此方法比较特殊 用于自定义验证逻辑 34 | * 35 | * @param value 参数值 36 | * @param beanName 实现了Validator接口的具体验证器的bean名称,所以需要能在spring容器里找到,之所以不采用反射原因 37 | * 运行时类加载,反射等带来的性能开销,而spring在启动阶段就进行扫描并管理了。 38 | * @return 39 | */ 40 | public static Boolean customValidate(Object value, String beanName) { 41 | ParamValidator bean = SpringContextHolder.getBean(beanName); 42 | if (bean == null) { 43 | throw new IllegalArgumentException("invalied bean, this bean can not found in spring context"); 44 | } 45 | Function func = bean::validate; 46 | return func.apply(value); 47 | } 48 | 49 | 50 | /** 51 | * 判断value == null 52 | * 53 | * @param value 字段值 54 | * @param express 这里不需要,只是为了参数统一 55 | * @return true or false 56 | */ 57 | public static Boolean isNull(Object value, String express) { 58 | if (null != value) { 59 | return Boolean.FALSE; 60 | } 61 | return Boolean.TRUE; 62 | } 63 | 64 | 65 | /** 66 | * 是否手机号 67 | * 68 | * @param value 参数值 69 | * @param express 空 70 | * @return 71 | */ 72 | public static Boolean isMobilePhone(Object value, String express) { 73 | if (null == value) { 74 | return Boolean.FALSE; 75 | } 76 | return MOBILE.matcher(String.valueOf(value)).matches(); 77 | } 78 | 79 | /** 80 | * 是否数字类型 包括小数 81 | * 82 | * @param value 83 | * @param express 84 | * @return 85 | */ 86 | public static Boolean isNumber(Object value, String express) { 87 | if (null == value) { 88 | return Boolean.FALSE; 89 | } 90 | if (value instanceof Number) { 91 | return Boolean.TRUE; 92 | } 93 | return NUMBER_CODE.matcher(String.valueOf(value)).matches(); 94 | } 95 | 96 | /** 97 | * 判断value != null 98 | * 99 | * @param value 字段值 100 | * @param express 这里不需要,只是为了参数统一 101 | * @return true or false 102 | */ 103 | public static Boolean isNotNull(Object value, String express) { 104 | if (null == value) { 105 | return Boolean.FALSE; 106 | } 107 | return Boolean.TRUE; 108 | } 109 | 110 | 111 | /** 112 | * 判断value ==null || length size <= 0 113 | * 支持字符串判断 114 | * 支持集合判断 115 | * 116 | * @param value 117 | * @param express 118 | * @return 119 | */ 120 | public static Boolean isEmpty(Object value, String express) { 121 | return !isNotEmpty(value, express); 122 | } 123 | 124 | 125 | /** 126 | * 判断value !=null && length、size > 0 127 | * 支持字符串判断 128 | * 支持集合判断 129 | * 130 | * @param value 131 | * @param express 132 | * @return 133 | */ 134 | public static Boolean isNotEmpty(Object value, String express) { 135 | if (isNull(value, express)) { 136 | return Boolean.FALSE; 137 | } 138 | if (value instanceof String && "".equals(((String) value).trim())) { 139 | return Boolean.FALSE; 140 | } 141 | if (value instanceof Collection && (value == null || ((Collection) value).size() == 0)) { 142 | return Boolean.FALSE; 143 | } 144 | return Boolean.TRUE; 145 | } 146 | 147 | 148 | /** 149 | * 判断参数是否是 true 150 | * 支持Boolean类型 151 | * 支持String类型 152 | * 153 | * @param value 154 | * @param express 155 | * @return 156 | */ 157 | public static Boolean isTrue(Object value, String express) { 158 | if (isNull(value, express)) { 159 | return Boolean.FALSE; 160 | } 161 | if (value instanceof Boolean) { 162 | return (Boolean) value; 163 | } 164 | if (value instanceof String) { 165 | try { 166 | return Boolean.parseBoolean((String) value); 167 | } catch (Exception e) { 168 | return Boolean.FALSE; 169 | } 170 | } 171 | return Boolean.FALSE; 172 | } 173 | 174 | /** 175 | * 判断参数是否是 false 176 | * 支持Boolean类型 177 | * 支持String类型 178 | * 179 | * @param value 180 | * @param express 181 | * @return 182 | */ 183 | public static Boolean isFalse(Object value, String express) { 184 | return !isTrue(value, express); 185 | } 186 | 187 | 188 | /** 189 | * 判断参数是否是一个日期 190 | * 支持Date类型 191 | * 支持LocalDate类型 192 | * 支持String类型,yyyy-MM-dd、yyyyMMdd、yyyy/MM/dd格式; 默认仅支持yyyy-MM-dd 193 | * 194 | * @param value 195 | * @param express 时间格式 196 | * @return 197 | */ 198 | public static Boolean isDate(Object value, String express) { 199 | if (isNull(value, express)) { 200 | return Boolean.FALSE; 201 | } 202 | if (express == null || "".equals(express)) { 203 | express = "yyyy-MM-dd"; 204 | } 205 | if (value instanceof String) { 206 | String v = ((String) value); 207 | try { 208 | LocalDate.parse(v, DateTimeFormatter.ofPattern(express)); 209 | return Boolean.TRUE; 210 | } catch (Exception e) { 211 | return Boolean.FALSE; 212 | } 213 | } 214 | if (value instanceof Date) { 215 | return Boolean.TRUE; 216 | } 217 | if (value instanceof LocalDate) { 218 | return Boolean.TRUE; 219 | } 220 | return Boolean.FALSE; 221 | } 222 | 223 | 224 | /** 225 | * 判断参数是否是一个日期 226 | * 支持Date类型 227 | * 支持LocalDateTime类型 228 | * 支持String类型,yyyy-MM-dd HH:mm:ss、yyyyMMddHHmmss、yyyy/MM/dd HH:mm:ss格式; 默认仅支持yyyy-MM-dd HH:mm:ss 229 | * 230 | * @param value 231 | * @param express 时间格式 232 | * @return 233 | */ 234 | public static Boolean isDateTime(Object value, String express) { 235 | if (isNull(value, express)) { 236 | return Boolean.FALSE; 237 | } 238 | if (express == null || "".equals(express)) { 239 | express = "yyyy-MM-dd HH:mm:ss"; 240 | } 241 | // 通常json格式参数,都是以字符串类型传递,优先判断 242 | if (value instanceof String) { 243 | //.replaceAll("[-/]", ""); // 验证参数,不能处理掉所有异常的符号 244 | String v = ((String) value); 245 | try { 246 | LocalDateTime.parse(v, DateTimeFormatter.ofPattern(express)); 247 | return Boolean.TRUE; 248 | } catch (Exception e) { 249 | return Boolean.FALSE; 250 | } 251 | } 252 | if (value instanceof Date) { 253 | return Boolean.TRUE; 254 | } 255 | if (value instanceof LocalDateTime) { 256 | return Boolean.TRUE; 257 | } 258 | return Boolean.FALSE; 259 | } 260 | 261 | /** 262 | * 判断参数是否是一个过去时间 263 | * 支持Date类型 264 | * 支持LocalDate类型 265 | * 支持LocalDateTime类型 266 | * 支持String类型,yyyy-MM-dd、yyyyMMdd、yyyy/MM/dd格式; 默认仅支持yyyy-MM-dd 267 | * 支持String类型,yyyy-MM-dd HH:mm:ss、yyyyMMddHHmmss、yyyy/MM/dd HH:mm:ss格式; 默认仅支持yyyy-MM-dd HH:mm:ss 268 | * 269 | * @param value 270 | * @param express 时间格式 271 | * @return 272 | */ 273 | public static Boolean isPast(Object value, String express) { 274 | if (isNull(value, express)) { 275 | return Boolean.FALSE; 276 | } 277 | if (express == null || "".equals(express)) { 278 | express = "yyyy-MM-dd HH:mm:ss"; 279 | } 280 | if (value instanceof String) { // 通常json格式参数,都是以字符串类型传递,优先判断 281 | String v = ((String) value); //.replaceAll("[-/]", ""); // 验证参数,不能处理掉所有异常的符号 282 | try { 283 | LocalDateTime ldt = LocalDateTime.parse(v, DateTimeFormatter.ofPattern(express)); 284 | return LocalDateTime.now().isAfter(ldt); 285 | } catch (Exception e) { 286 | return Boolean.FALSE; 287 | } 288 | } 289 | if (value instanceof Date) { 290 | return new Date().after((Date) value); 291 | } 292 | if (value instanceof LocalDate) { 293 | return LocalDate.now().isAfter((LocalDate) value); 294 | } 295 | if (value instanceof LocalDateTime) { 296 | return LocalDateTime.now().isAfter((LocalDateTime) value); 297 | } 298 | return Boolean.FALSE; 299 | } 300 | 301 | /** 302 | * 判断参数是否是一个将来时间 303 | * 支持Date类型 304 | * 支持LocalDate类型 305 | * 支持LocalDateTime类型 306 | * 支持String类型,yyyy-MM-dd、yyyyMMdd、yyyy/MM/dd格式; 默认仅支持yyyy-MM-dd 307 | * 支持String类型,yyyy-MM-dd HH:mm:ss、yyyyMMddHHmmss、yyyy/MM/dd HH:mm:ss格式; 默认仅支持yyyy-MM-dd HH:mm:ss 308 | * 309 | * @param value 310 | * @param express 时间格式 311 | * @return 312 | */ 313 | public static Boolean isFuture(Object value, String express) { 314 | if (isNull(value, express)) { 315 | return Boolean.FALSE; 316 | } 317 | if (express == null || "".equals(express)) { 318 | express = "yyyy-MM-dd HH:mm:ss"; 319 | } 320 | // 通常json格式参数,都是以字符串类型传递,优先判断 321 | if (value instanceof String) { 322 | // .replaceAll("[-/]", ""); 验证参数,不能处理掉所有异常的符号 323 | String v = ((String) value); 324 | try { 325 | LocalDateTime ldt = LocalDateTime.parse(v, DateTimeFormatter.ofPattern(express)); 326 | return LocalDateTime.now().isBefore(ldt); 327 | } catch (Exception e) { 328 | return Boolean.FALSE; 329 | } 330 | } 331 | if (value instanceof Date) { 332 | return new Date().before((Date) value); 333 | } 334 | if (value instanceof LocalDate) { 335 | return LocalDate.now().isBefore((LocalDate) value); 336 | } 337 | if (value instanceof LocalDateTime) { 338 | return LocalDateTime.now().isBefore((LocalDateTime) value); 339 | } 340 | return Boolean.FALSE; 341 | } 342 | 343 | 344 | /** 345 | * 判断是否是今天的日期 346 | * 支持Date类型 347 | * 支持LocalDate类型 348 | * 支持String类型,默认仅支持yyyy-MM-dd 349 | * 350 | * @param value 351 | * @param express 时间格式 352 | * @return 353 | */ 354 | public static Boolean isToday(Object value, String express) { 355 | if (isNull(value, express)) { 356 | return Boolean.FALSE; 357 | } 358 | if (express == null || "".equals(express)) { 359 | express = "yyyy-MM-dd HH:mm:ss"; 360 | } 361 | // 通常json格式参数,都是以字符串类型传递,优先判断 362 | if (value instanceof String) { 363 | // .replaceAll("[-/]", ""); // 验证参数,不能处理掉所有异常的符号 364 | String v = ((String) value); 365 | try { 366 | LocalDate ld = LocalDate.parse(v, DateTimeFormatter.ofPattern(express)); 367 | return LocalDate.now().equals(ld); 368 | } catch (Exception e) { 369 | return Boolean.FALSE; 370 | } 371 | } 372 | if (value instanceof Date) { 373 | return new Date().equals(value); 374 | } 375 | if (value instanceof LocalDate) { 376 | return LocalDate.now().equals(value); 377 | } 378 | return Boolean.FALSE; 379 | } 380 | 381 | 382 | /** 383 | * 判断是否是邮箱 384 | * 使用正则表达式判断 385 | * 386 | * @param value 387 | * @param express 388 | * @return 389 | */ 390 | public static Boolean isEmail(Object value, String express) { 391 | if (isNull(value, express)) { 392 | return Boolean.FALSE; 393 | } 394 | if (value instanceof String) { 395 | return EMAIL.matcher(String.valueOf(value)).matches(); 396 | } 397 | return Boolean.FALSE; 398 | } 399 | 400 | /** 401 | * 判断参数的取值范围,逗号隔开,无空格;闭区间 402 | * 支持Integer、Long、Short、Float、Double、BigDecimal 403 | * 404 | * @param value 405 | * @param rangeStr 406 | * @return 407 | */ 408 | public static Boolean inRange(Object value, String rangeStr) { 409 | if (isNull(value, rangeStr)) { 410 | return Boolean.FALSE; 411 | } 412 | if (null == rangeStr || "".equals(rangeStr)) { 413 | return Boolean.FALSE; 414 | } 415 | if (value instanceof Integer) { 416 | Integer begin = Integer.valueOf(rangeStr.split(",")[0]); 417 | Integer end = Integer.valueOf(rangeStr.split(",")[1]); 418 | Integer v = ((Integer) value); 419 | return begin <= v && v <= end; 420 | } 421 | if (value instanceof Long) { 422 | Long begin = Long.valueOf(rangeStr.split(",")[0]); 423 | Long end = Long.valueOf(rangeStr.split(",")[1]); 424 | Long v = ((Long) value); 425 | return begin <= v && v <= end; 426 | } 427 | if (value instanceof Short) { 428 | Short begin = Short.valueOf(rangeStr.split(",")[0]); 429 | Short end = Short.valueOf(rangeStr.split(",")[1]); 430 | Short v = ((Short) value); 431 | return begin <= v && v <= end; 432 | } 433 | if (value instanceof Float) { 434 | Float begin = Float.valueOf(rangeStr.split(",")[0]); 435 | Float end = Float.valueOf(rangeStr.split(",")[1]); 436 | Float v = ((Float) value); 437 | return begin <= v && v <= end; 438 | } 439 | if (value instanceof Double) { 440 | Double begin = Double.valueOf(rangeStr.split(",")[0]); 441 | Double end = Double.valueOf(rangeStr.split(",")[1]); 442 | Double v = ((Double) value); 443 | return begin <= v && v <= end; 444 | } 445 | if (value instanceof BigDecimal) { 446 | BigDecimal begin = new BigDecimal(rangeStr.split(",")[0]); 447 | BigDecimal end = new BigDecimal(rangeStr.split(",")[1]); 448 | BigDecimal v = ((BigDecimal) value); 449 | return begin.compareTo(v) <= 0 && v.compareTo(end) <= 0; 450 | } 451 | 452 | return Boolean.FALSE; 453 | } 454 | 455 | 456 | /** 457 | * 等价于 !Range 458 | * 459 | * @param value 460 | * @param rangeStr 461 | * @return 462 | */ 463 | public static Boolean outRange(Object value, String rangeStr) { 464 | return !inRange(value, rangeStr); 465 | } 466 | 467 | 468 | /** 469 | * 判断参数的取值范围,逗号隔开,无空格;闭区间 470 | * 判断String的length范围, rangeStr取值举例:"6,18" 471 | * 472 | * @param value 473 | * @param rangeStr 474 | * @return 475 | */ 476 | public static Boolean inLength(Object value, String rangeStr) { 477 | if (isNull(value, rangeStr)) { 478 | return Boolean.FALSE; 479 | } 480 | if (null == rangeStr || "".equals(rangeStr)) { 481 | return Boolean.FALSE; 482 | } 483 | String spliter = ","; 484 | if (value instanceof String) { 485 | Integer begin = null; 486 | Integer end = null; 487 | if (!rangeStr.contains(spliter)) { 488 | begin = 0; 489 | } else { 490 | begin = Integer.valueOf(rangeStr.split(spliter)[0]); 491 | } 492 | if (begin == 0) { 493 | end = Integer.valueOf(rangeStr); 494 | } else { 495 | end = Integer.valueOf(rangeStr.split(spliter)[1]); 496 | } 497 | Integer v = ((String) value).length(); 498 | return begin <= v && v <= end; 499 | } 500 | return Boolean.FALSE; 501 | } 502 | 503 | 504 | /** 505 | * 判断参数是否在枚举的数据中, 枚举的表达式用 英文逗号隔开,无空格,如: "男,女,太监" 506 | * 校验过程,不在对表达式进行校验,所以请确保表达式的格式正确 507 | * 支持String 508 | * 支持Integer Short Long 509 | * 510 | * @param value 511 | * @param enumStr 512 | * @return 513 | */ 514 | public static Boolean inEnum(Object value, String enumStr) { 515 | if (isNull(value, null)) { 516 | return Boolean.FALSE; 517 | } 518 | if (null == enumStr || "".equals(enumStr)) { 519 | return Boolean.FALSE; 520 | } 521 | String[] array = enumStr.split(","); 522 | Set set = new HashSet<>(Arrays.asList(array)); 523 | return set.contains(value.toString()); 524 | } 525 | 526 | 527 | /** 528 | * 是否大于指定值 529 | * 支持Integer、Long、Short、Float、Double、BigDecimal 530 | * 支持String,判断length值 531 | * 支持Collection,判断size的值 532 | * 533 | * @param value 534 | * @param express 指定值 535 | * @return 536 | */ 537 | public static Boolean isGreaterThan(Object value, String express) { 538 | if (value == null) { 539 | return Boolean.FALSE; 540 | } 541 | if (value instanceof Integer) { 542 | return ((Integer) value) > Integer.valueOf(express); 543 | } 544 | if (value instanceof Long) { 545 | return ((Long) value) > Long.valueOf(express); 546 | } 547 | if (value instanceof Short) { 548 | return ((Short) value) > Short.valueOf(express); 549 | } 550 | if (value instanceof Float) { 551 | return ((Float) value) > Float.valueOf(express); 552 | } 553 | if (value instanceof Double) { 554 | return ((Double) value) > Double.valueOf(express); 555 | } 556 | if (value instanceof BigDecimal) { 557 | return ((BigDecimal) value).compareTo(new BigDecimal(express)) > 0; 558 | } 559 | if (value instanceof String) { 560 | return ((String) value).length() > Integer.valueOf(express); 561 | } 562 | if (value instanceof Collection) { 563 | return ((Collection) value).size() > Integer.valueOf(express); 564 | } 565 | return Boolean.FALSE; 566 | } 567 | 568 | 569 | /** 570 | * 是否大于等于 571 | * 支持Integer、Long、Short、Float、Double、BigDecimal 572 | * 支持String,判断length值 573 | * 支持Collection,判断size的值 574 | * 575 | * @param value 576 | * @param express 指定值 577 | * @return 578 | */ 579 | public static Boolean isGreaterThanEqual(Object value, String express) { 580 | if (value == null) { 581 | return Boolean.FALSE; 582 | } 583 | if (value instanceof Integer) { 584 | return ((Integer) value) >= Integer.valueOf(express); 585 | } 586 | if (value instanceof Long) { 587 | return ((Long) value) >= Long.valueOf(express); 588 | } 589 | if (value instanceof Short) { 590 | return ((Short) value) >= Short.valueOf(express); 591 | } 592 | if (value instanceof Float) { 593 | return ((Float) value) >= Float.valueOf(express); 594 | } 595 | if (value instanceof Double) { 596 | return ((Double) value) >= Double.valueOf(express); 597 | } 598 | if (value instanceof BigDecimal) { 599 | return ((BigDecimal) value).compareTo(new BigDecimal(express)) >= 0; 600 | } 601 | if (value instanceof String) { 602 | return ((String) value).length() >= Integer.valueOf(express); 603 | } 604 | if (value instanceof Collection) { 605 | return ((Collection) value).size() >= Integer.valueOf(express); 606 | } 607 | return Boolean.FALSE; 608 | 609 | } 610 | 611 | /** 612 | * 是否少于 613 | * 支持Integer、Long、Short、Float、Double、BigDecimal 614 | * 支持String,判断length值 615 | * 支持Collection,判断size的值 616 | * 617 | * @param value 618 | * @param express 指定值 619 | * @return 620 | */ 621 | public static Boolean isLessThan(Object value, String express) { 622 | if (value == null) { 623 | return Boolean.FALSE; 624 | } 625 | if (value instanceof Integer) { 626 | return ((Integer) value) < Integer.valueOf(express); 627 | } 628 | if (value instanceof Long) { 629 | return ((Long) value) < Long.valueOf(express); 630 | } 631 | if (value instanceof Short) { 632 | return ((Short) value) < Short.valueOf(express); 633 | } 634 | if (value instanceof Float) { 635 | return ((Float) value) < Float.valueOf(express); 636 | } 637 | if (value instanceof Double) { 638 | return ((Double) value) < Double.valueOf(express); 639 | } 640 | if (value instanceof BigDecimal) { 641 | return ((BigDecimal) value).compareTo(new BigDecimal(express)) < 0; 642 | } 643 | if (value instanceof String) { 644 | return ((String) value).length() < Integer.valueOf(express); 645 | } 646 | if (value instanceof Collection) { 647 | return ((Collection) value).size() < Integer.valueOf(express); 648 | } 649 | return Boolean.FALSE; 650 | } 651 | 652 | /** 653 | * 是否少于等于 654 | * 支持Integer、Long、Short、Float、Double、BigDecimal 655 | * 支持String,判断length值 656 | * 支持Collection,判断size的值 657 | * 658 | * @param value 659 | * @param express 指定值 660 | * @return 661 | */ 662 | public static Boolean isLessThanEqual(Object value, String express) { 663 | if (value == null) { 664 | return Boolean.FALSE; 665 | } 666 | if (value instanceof Integer) { 667 | return ((Integer) value) <= Integer.valueOf(express); 668 | } 669 | if (value instanceof Long) { 670 | return ((Long) value) <= Long.valueOf(express); 671 | } 672 | if (value instanceof Short) { 673 | return ((Short) value) <= Short.valueOf(express); 674 | } 675 | if (value instanceof Float) { 676 | return ((Float) value) <= Float.valueOf(express); 677 | } 678 | if (value instanceof Double) { 679 | return ((Double) value) <= Double.valueOf(express); 680 | } 681 | if (value instanceof BigDecimal) { 682 | return ((BigDecimal) value).compareTo(new BigDecimal(express)) <= 0; 683 | } 684 | if (value instanceof String) { 685 | return ((String) value).length() <= Integer.valueOf(express); 686 | } 687 | if (value instanceof Collection) { 688 | return ((Collection) value).size() <= Integer.valueOf(express); 689 | } 690 | return Boolean.FALSE; 691 | } 692 | 693 | /** 694 | * 判断是否 notEqual指定的值 695 | * 支持String、Integer、Long、Short、Float、Double、BigDecimal 696 | * 支持Collection,判断size的值 697 | * 698 | * @param value 699 | * @param express 700 | * @return 701 | */ 702 | public static Boolean isNotEqual(Object value, String express) { 703 | return !isEqual(value, express); 704 | } 705 | 706 | /** 707 | * 判断文件大小 单位 KB 708 | * 支持 File对象 MultipartFile 对象 或对应的集合 数组都可以 709 | * 710 | * @param value 文件 File对象 MultipartFile 对象 或对应的集合 数组都可以 711 | * @param express 大小值 默认10M 例如 如果你的文件大小不希望超过2M 传 2048即可 712 | * @return 713 | */ 714 | public static Boolean isSuitableFileLength(Object value, String express) { 715 | if (value == null || "".equals(value)) { 716 | return Boolean.FALSE; 717 | } 718 | List lens = new ArrayList(); 719 | if (value instanceof File || value instanceof MultipartFile) { 720 | judgeLen(value, lens); 721 | } 722 | if (value instanceof Collection) { 723 | ((Collection) value).forEach(e -> { 724 | judgeLen(e, lens); 725 | }); 726 | } 727 | if (value.getClass().isArray()) { 728 | Object[] objs = (Object[]) value; 729 | for (int i = 0; i < objs.length; i++) { 730 | Object tmpvalue = objs[i]; 731 | judgeLen(tmpvalue, lens); 732 | } 733 | } 734 | if (lens.size() == 0) { 735 | return Boolean.FALSE; 736 | } 737 | 738 | return realJudgeFileLen(lens, express); 739 | 740 | } 741 | 742 | private static Boolean realJudgeFileLen(List lens, String express) { 743 | long defaultLen = DEFAULT_FILE_SIZE; 744 | if (express != null && !"".equals(express)) { 745 | defaultLen = Long.parseLong(express); 746 | } 747 | 748 | // 单位 KB 749 | for (Long len : lens) { 750 | if (len / 1024 > defaultLen) { 751 | return Boolean.FALSE; 752 | } 753 | } 754 | return Boolean.TRUE; 755 | } 756 | 757 | 758 | /** 759 | * 是否是合法的文件后缀 760 | * String,File对象 MultipartFile 对象 或对应的集合 数组都可以 761 | * 762 | * @param value 文件 763 | * @param express 自定义后缀 多个逗号分隔即可 例如 jpg,png(会覆盖默认的后缀 ) 764 | * @return 765 | */ 766 | public static Boolean isSuitableFileSuffix(Object value, String express) { 767 | if (value == null || "".equals(value)) { 768 | return Boolean.FALSE; 769 | } 770 | 771 | List names = new ArrayList(); 772 | if (value instanceof File || value instanceof MultipartFile || value instanceof String) { 773 | judge(value, names); 774 | } 775 | if (value instanceof Collection) { 776 | ((Collection) value).forEach(e -> { 777 | judge(e, names); 778 | }); 779 | } 780 | if (value.getClass().isArray()) { 781 | Object[] objs = (Object[]) value; 782 | for (int i = 0; i < objs.length; i++) { 783 | Object tmpvalue = objs[i]; 784 | judge(tmpvalue, names); 785 | } 786 | } 787 | if (names.size() == 0) { 788 | return Boolean.FALSE; 789 | } 790 | 791 | return realJudgeFileSuffix(names, express); 792 | } 793 | 794 | private static Boolean realJudgeFileSuffix(List names, String express) { 795 | String[] suffixs = null; 796 | if (express != null && !"".equals(express)) { 797 | suffixs = express.split(","); 798 | } else { 799 | suffixs = DEFAULT_ALLOWED_EXTENSION; 800 | } 801 | List suffixList = Arrays.asList(suffixs); 802 | for (Object fileName : names) { 803 | String fileName1 = (String) fileName; 804 | if (fileName1.contains(".")) { 805 | String fileSuffix = fileName1.split("\\.")[1]; 806 | if (!(fileSuffix != null && suffixList.contains(fileSuffix))) { 807 | return Boolean.FALSE; 808 | } 809 | } else { 810 | return Boolean.FALSE; 811 | } 812 | } 813 | return Boolean.TRUE; 814 | } 815 | 816 | private static void judgeLen(Object tmpvalue, List names) { 817 | if (tmpvalue instanceof File) { 818 | long length = ((File) tmpvalue).length(); 819 | names.add(length); 820 | } else if (tmpvalue instanceof MultipartFile) { 821 | long size = ((MultipartFile) tmpvalue).getSize(); 822 | names.add(size); 823 | } else { 824 | throw new ParamsCheckException("the field type is wrong, we need a File or MultipartFile "); 825 | } 826 | } 827 | 828 | private static void judge(Object tmpvalue, List names) { 829 | if (tmpvalue instanceof File) { 830 | String filename = ((File) tmpvalue).getName(); 831 | names.add(filename); 832 | } else if (tmpvalue instanceof MultipartFile) { 833 | String filename = ((MultipartFile) tmpvalue).getOriginalFilename(); 834 | names.add(filename); 835 | } else if (tmpvalue instanceof String) { 836 | names.add(tmpvalue); 837 | } else { 838 | throw new ParamsCheckException("the field type is wrong, we need a File or MultipartFile or String "); 839 | } 840 | } 841 | 842 | 843 | /** 844 | * 判断是否Equal指定的值 845 | * 支持String、Integer、Long、Short、Float、Double、BigDecimal 846 | * 支持Collection,判断size的值 847 | * 848 | * @param value 849 | * @param express 850 | * @return 851 | */ 852 | public static Boolean isEqual(Object value, String express) { 853 | if (value == null) { 854 | return Boolean.FALSE; 855 | } 856 | if (value instanceof String) { 857 | return ((String) value).equals(express); 858 | } 859 | if (value instanceof Integer) { 860 | return ((Integer) value).equals(Integer.valueOf(express)); 861 | } 862 | if (value instanceof Long) { 863 | return ((Long) value).equals(Long.valueOf(express)); 864 | } 865 | if (value instanceof Short) { 866 | return ((Short) value).equals(Short.valueOf(express)); 867 | } 868 | if (value instanceof Float) { 869 | return ((Float) value).equals(Float.valueOf(express)); 870 | } 871 | if (value instanceof Double) { 872 | return ((Double) value).equals(Double.valueOf(express)); 873 | } 874 | if (value instanceof BigDecimal) { 875 | return ((BigDecimal) value).compareTo(new BigDecimal(express)) == 0; 876 | } 877 | if (value instanceof Collection) { 878 | return ((Collection) value).size() == Integer.valueOf(express); 879 | } 880 | return Boolean.FALSE; 881 | } 882 | 883 | 884 | /** 885 | * 判断String是否满足正则表达式 886 | * 887 | * @param value 888 | * @param regEx 正则表达式 889 | * @return 890 | */ 891 | public static Boolean isPattern(Object value, String regEx) { 892 | if (isNull(value, null)) { 893 | return Boolean.FALSE; 894 | } 895 | if (value instanceof String) { 896 | Pattern p = Pattern.compile(regEx); 897 | Matcher m = p.matcher((String) value); 898 | if (m.matches()) { 899 | return Boolean.TRUE; 900 | } 901 | } 902 | return Boolean.FALSE; 903 | } 904 | 905 | 906 | /** 907 | * 时间毫秒值 908 | * 909 | * @param value 910 | * @param regEx 911 | * @return 912 | */ 913 | public static Boolean isTimeMillSeconds(Object value, String regEx) { 914 | Boolean number = isNumber(value, regEx); 915 | if (number) { 916 | if (String.valueOf(value).length() == 13) { 917 | return true; 918 | } 919 | } 920 | return false; 921 | 922 | } 923 | 924 | /** 925 | * 是否是身份证号 926 | * 927 | * @param value 928 | * @param regEx 929 | * @return 930 | */ 931 | public static Boolean isIdCard(Object value, String regEx) { 932 | if (value == null) { 933 | return Boolean.FALSE; 934 | } 935 | return CITIZEN_ID.matcher(String.valueOf(value)).matches(); 936 | } 937 | 938 | // 939 | // /** 940 | // * 是否是有效的统一社会信用代码 941 | // * 942 | // * @param value 943 | // * @param regEx 944 | // * @return 945 | // */ 946 | // public static Boolean isCreditCode(Object value, String regEx) { 947 | // if (value == null) { 948 | // return Boolean.FALSE; 949 | // } 950 | // return Validator.isCreditCode(String.valueOf(value)); 951 | // } 952 | 953 | /** 954 | * 是否是中国邮编 955 | * 956 | * @param value 957 | * @param regEx 958 | * @return 959 | */ 960 | public static Boolean isChinesePostCode(Object value, String regEx) { 961 | if (value == null) { 962 | return Boolean.FALSE; 963 | } 964 | return ZIP_CODE.matcher(String.valueOf(value)).matches(); 965 | } 966 | 967 | /** 968 | * 是否是Ipv4 969 | * 970 | * @param value 971 | * @param regEx 972 | * @return 973 | */ 974 | public static Boolean isIpv4(Object value, String regEx) { 975 | if (value == null) { 976 | return Boolean.FALSE; 977 | } 978 | return IPV4.matcher(String.valueOf(value)).matches(); 979 | } 980 | 981 | /** 982 | * 是否是Ipv6 983 | * 984 | * @param value 985 | * @param regEx 986 | * @return 987 | */ 988 | public static Boolean isIpv6(Object value, String regEx) { 989 | if (value == null) { 990 | return Boolean.FALSE; 991 | } 992 | return IPV6.matcher(String.valueOf(value)).matches(); 993 | } 994 | 995 | /** 996 | * 是否是汉字 997 | * 998 | * @param value 999 | * @param regEx 1000 | * @return 1001 | */ 1002 | public static Boolean isChinese(Object value, String regEx) { 1003 | if (value == null) { 1004 | return Boolean.FALSE; 1005 | } 1006 | return CHINESES.matcher(String.valueOf(value)).matches(); 1007 | } 1008 | 1009 | /** 1010 | * 验证是否为英文字母 、数字和下划线 1011 | * 1012 | * @param value 1013 | * @param regEx 1014 | * @return 1015 | */ 1016 | public static Boolean isGeneral(Object value, String regEx) { 1017 | if (value == null) { 1018 | return Boolean.FALSE; 1019 | } 1020 | return GENERAL.matcher(String.valueOf(value)).matches(); 1021 | } 1022 | 1023 | /** 1024 | * 验证是否为MAC地址 1025 | * 1026 | * @param value 1027 | * @param regEx 1028 | * @return 1029 | */ 1030 | public static Boolean isMac(Object value, String regEx) { 1031 | if (value == null) { 1032 | return Boolean.FALSE; 1033 | } 1034 | return MAC_ADDRESS.matcher(String.valueOf(value)).matches(); 1035 | } 1036 | 1037 | /** 1038 | * 验证是否为中国车牌号 1039 | * 1040 | * @param value 1041 | * @param regEx 1042 | * @return 1043 | */ 1044 | public static Boolean isPlateNumber(Object value, String regEx) { 1045 | if (value == null) { 1046 | return Boolean.FALSE; 1047 | } 1048 | return PLATE_NUMBER.matcher(String.valueOf(value)).matches(); 1049 | } 1050 | 1051 | /** 1052 | * 验证是否为URL 1053 | * 1054 | * @param value 1055 | * @param regEx 1056 | * @return 1057 | */ 1058 | public static Boolean isUrl(Object value, String regEx) { 1059 | if (value == null) { 1060 | return Boolean.FALSE; 1061 | } 1062 | try { 1063 | new java.net.URL(String.valueOf(value)); 1064 | } catch (MalformedURLException e) { 1065 | return false; 1066 | } 1067 | return true; 1068 | } 1069 | 1070 | /** 1071 | * 验证是否是图书的合法的ISBN号码 包括10或者13位的ISBN 1072 | * 1073 | * @param value 1074 | * @param regEx 1075 | * @return 1076 | * @since 0.5.0 1077 | */ 1078 | @SuppressWarnings("warn") 1079 | public static Boolean isISBN(Object value, String regEx) { 1080 | if (value == null) { 1081 | return Boolean.FALSE; 1082 | } 1083 | return RegexPattern.ISBN_REGEX.matcher(String.valueOf(value)).matches(); 1084 | } 1085 | 1086 | /** 1087 | * 是否是银行卡号 1088 | * Luhn算法来验证: 1089 | * 1、从卡号最后一位数字开始,逆向将奇数位(1、3、5等等)相加。 1090 | * 2、从卡号最后一位数字开始,逆向将偶数位数字,先乘以2(如果乘积为两位数,则将其减去9),再求和。 1091 | * 3、将奇数位总和加上偶数位总和,结果应该可以被10整除。 1092 | * 1093 | * @param value 1094 | * @return 1095 | */ 1096 | public static Boolean isBankNumber(Object value, String regEx) { 1097 | if (value == null) { 1098 | return Boolean.FALSE; 1099 | } 1100 | String number = String.valueOf(value); 1101 | if (number.length() != 16 && number.length() != 19) { 1102 | return false; 1103 | } 1104 | if (!number.matches("\\d+")) { 1105 | return false; 1106 | } 1107 | 1108 | char[] digits = number.toCharArray(); 1109 | int len = number.length(); 1110 | int numSum = 0; 1111 | for (int i = len - 1, j = 1; i >= 0; i--, j++) { 1112 | int value0 = digits[i] - '0'; 1113 | if (j % 2 == 0) { 1114 | value0 *= 2; 1115 | if (value0 > 9) { 1116 | value0 -= 9; 1117 | } 1118 | } 1119 | numSum += value0; 1120 | } 1121 | return numSum % 10 == 0; 1122 | } 1123 | 1124 | /** 1125 | * 验证是否为UUID 1126 | * 包括带横线标准格式和不带横线的简单模式 1127 | * 1128 | * @param value 1129 | * @param regEx 1130 | * @return 1131 | */ 1132 | @SuppressWarnings("warn") 1133 | public static Boolean isUUID(Object value, String regEx) { 1134 | if (value == null) { 1135 | return Boolean.FALSE; 1136 | } 1137 | return RegexPattern.UUID.matcher(String.valueOf(value)).matches() || UUID_SIMPLE.matcher(String.valueOf(value)).matches(); 1138 | } 1139 | 1140 | /** 1141 | * 验证是否为生日
1142 | * 只支持以下几种格式: 1143 | *
    1144 | *
  • yyyyMMdd
  • 1145 | *
  • yyyy-MM-dd
  • 1146 | *
  • yyyy/MM/dd
  • 1147 | *
  • yyyy.MM.dd
  • 1148 | *
  • yyyy年MM月dd日
  • 1149 | *
1150 | * 1151 | * @param value 值 1152 | * @return 是否为生日 1153 | */ 1154 | public static Boolean isBirthday(Object value, String regEx) { 1155 | if (value == null) { 1156 | return Boolean.FALSE; 1157 | } 1158 | final Matcher matcher = BIRTHDAY.matcher(String.valueOf(value)); 1159 | if (matcher.find()) { 1160 | int year = Integer.parseInt(matcher.group(1)); 1161 | int month = Integer.parseInt(matcher.group(3)); 1162 | int day = Integer.parseInt(matcher.group(5)); 1163 | // 验证年 1164 | Calendar calendar = Calendar.getInstance(); 1165 | int thisYear = calendar.get(Calendar.YEAR); 1166 | if (year < 1900 || year > thisYear) { 1167 | return false; 1168 | } 1169 | 1170 | // 验证月 1171 | if (month < 1 || month > 12) { 1172 | return false; 1173 | } 1174 | 1175 | // 验证日 1176 | if (day < 1 || day > 31) { 1177 | return false; 1178 | } 1179 | // 检查几个特殊月的最大天数 1180 | if (day == 31 && (month == 4 || month == 6 || month == 9 || month == 11)) { 1181 | return false; 1182 | } 1183 | return true; 1184 | } 1185 | return false; 1186 | } 1187 | 1188 | /** 1189 | * 相关正则表达式 1190 | */ 1191 | public static class RegexPattern { 1192 | /** 1193 | * 邮箱 1194 | */ 1195 | public final static Pattern EMAIL = Pattern.compile("^([a-z0-9A-Z]+[-|\\.]?)+[a-z0-9A-Z]@([a-z0-9A-Z]+(-[a-z0-9A-Z]+)?\\.)+[a-zA-Z]{2,}$"); 1196 | 1197 | /** 1198 | * 英文字母 、数字和下划线 1199 | */ 1200 | public final static Pattern GENERAL = Pattern.compile("^\\w+$"); 1201 | 1202 | /** 1203 | * 中文汉字 1204 | */ 1205 | public final static Pattern CHINESES = Pattern.compile("[\u4E00-\u9FFF]+"); 1206 | 1207 | /** 1208 | * IP v4 1209 | */ 1210 | public final static Pattern IPV4 = Pattern.compile("\\b((?!\\d\\d\\d)\\d+|1\\d\\d|2[0-4]\\d|25[0-5])\\.((?!\\d\\d\\d)\\d+|1\\d\\d|2[0-4]\\d|25[0-5])\\.((?!\\d\\d\\d)\\d+|1\\d\\d|2[0-4]\\d|25[0-5])\\.((?!\\d\\d\\d)\\d+|1\\d\\d|2[0-4]\\d|25[0-5])\\b"); 1211 | /** 1212 | * IP v6 1213 | */ 1214 | public final static Pattern IPV6 = Pattern.compile("(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]+|::(ffff(:0{1,4})?:)?((25[0-5]|(2[0-4]|1?[0-9])?[0-9])\\.){3}(25[0-5]|(2[0-4]|1?[0-9])?[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1?[0-9])?[0-9])\\.){3}(25[0-5]|(2[0-4]|1?[0-9])?[0-9]))"); 1215 | /** 1216 | * 货币 1217 | */ 1218 | public final static Pattern MONEY = Pattern.compile("^(\\d+(?:\\.\\d+)?)$"); 1219 | 1220 | /** 1221 | * 移动电话 1222 | */ 1223 | public final static Pattern MOBILE = Pattern.compile("(?:0|86|\\+86)?1[3456789]\\d{9}"); 1224 | /** 1225 | * 18位身份证号码 1226 | */ 1227 | public final static Pattern CITIZEN_ID = Pattern.compile("[1-9]\\d{5}[1-2]\\d{3}((0\\d)|(1[0-2]))(([012]\\d)|3[0-1])\\d{3}(\\d|X|x)"); 1228 | /** 1229 | * 邮编 1230 | */ 1231 | public final static Pattern ZIP_CODE = Pattern.compile("[1-9]\\d{5}(?!\\d)"); 1232 | /** 1233 | * 生日 1234 | */ 1235 | public final static Pattern BIRTHDAY = Pattern.compile("^(\\d{2,4})([/\\-.年]?)(\\d{1,2})([/\\-.月]?)(\\d{1,2})日?$"); 1236 | 1237 | 1238 | /** 1239 | * 中文字、英文字母、数字和下划线 1240 | */ 1241 | public final static Pattern GENERAL_WITH_CHINESE = Pattern.compile("^[\u4E00-\u9FFF\\w]+$"); 1242 | 1243 | /** 1244 | * ISBN-10 OR ISBN-13 1245 | */ 1246 | public final static Pattern ISBN_REGEX = Pattern.compile("^(?:ISBN(?:-1[03])?:? )?(?=[0-9X]{10}$|(?=(?:[0-9]+[- ]){3})[- 0-9X]{13}$|97[89][0-9]{10}$|(?=(?:[0-9]+[- ]){4})[- 0-9]{17}$)(?:97[89][- ]?)?[0-9]{1,5}[- ]?[0-9]+[- ]?[0-9]+[- ]?[0-9X]$"); 1247 | 1248 | /** 1249 | * UUID 1250 | */ 1251 | public final static Pattern UUID = Pattern.compile("^[0-9a-z]{8}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{12}$"); 1252 | /** 1253 | * 不带横线的UUID 1254 | */ 1255 | public final static Pattern UUID_SIMPLE = Pattern.compile("^[0-9a-z]{32}$"); 1256 | /** 1257 | * MAC地址正则 1258 | */ 1259 | public static final Pattern MAC_ADDRESS = Pattern.compile("((?:[A-F0-9]{1,2}[:-]){5}[A-F0-9]{1,2})|(?:0x)(\\d{12})(?:.+ETHER)", Pattern.CASE_INSENSITIVE); 1260 | 1261 | /** 1262 | * 允许的文件后缀 1263 | */ 1264 | public static final String[] DEFAULT_ALLOWED_EXTENSION = { 1265 | // 图片 1266 | "bmp", "gif", "jpg", "jpeg", "png", "blob", "webp", "svg", "pcx", "ico", 1267 | // word excel powerpoint 1268 | "doc", "docx", "xls", "xlsx", "ppt", "pptx", "html", "htm", "txt", 1269 | // 压缩文件 1270 | "rar", "zip", "gz", "bz2", "7z", "tar.gz", 1271 | "xml", 1272 | // pdf 1273 | "pdf", 1274 | //视频 1275 | "swf", "flv", "mp3", "wav", "wma", "wmv", "mid", "avi", "mpg", 1276 | "asf", "rm", "rmvb", "mp4", "mov" 1277 | }; 1278 | /** 1279 | * 中国车牌号码(兼容新能源车牌) 1280 | */ 1281 | public final static Pattern PLATE_NUMBER = Pattern.compile( 1282 | //https://gitee.com/loolly/hutool/issues/I1B77H?from=project-issue 1283 | "^(([京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领][A-Z](([0-9]{5}[ABCDEFGHJK])|([ABCDEFGHJK]([A-HJ-NP-Z0-9])[0-9]{4})))|" + 1284 | //https://gitee.com/loolly/hutool/issues/I1BJHE?from=project-issue 1285 | "([京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领]\\d{3}\\d{1,3}[领])|" + 1286 | "([京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领][A-Z][A-HJ-NP-Z0-9]{4}[A-HJ-NP-Z0-9挂学警港澳使领]))$"); 1287 | 1288 | 1289 | /** 1290 | * 社会统一信用代码 1291 | *
1292 |          * 第一部分:登记管理部门代码1位 (数字或大写英文字母)
1293 |          * 第二部分:机构类别代码1位 (数字或大写英文字母)
1294 |          * 第三部分:登记管理机关行政区划码6位 (数字)
1295 |          * 第四部分:主体标识码(组织机构代码)9位 (数字或大写英文字母)
1296 |          * 第五部分:校验码1位 (数字或大写英文字母)
1297 |          * 
1298 | */ 1299 | public static final Pattern CREDIT_CODE = Pattern.compile("^[0-9A-HJ-NPQRTUWXY]{2}\\d{6}[0-9A-HJ-NPQRTUWXY]{10}$"); 1300 | 1301 | /** 1302 | * 数字 1303 | */ 1304 | public static final Pattern NUMBER_CODE = Pattern.compile("\\d+(\\.\\d+)?"); 1305 | 1306 | /** 1307 | * 默认文件大小 10M 1308 | */ 1309 | public static final Long DEFAULT_FILE_SIZE = 1024 * 1024 * 10L; 1310 | 1311 | } 1312 | 1313 | } 1314 | -------------------------------------------------------------------------------- /src/main/java/com/github/liangbaika/validate/utils/SpringContextHolder.java: -------------------------------------------------------------------------------- 1 | package com.github.liangbaika.validate.utils; 2 | 3 | import org.springframework.beans.BeansException; 4 | import org.springframework.context.ApplicationContext; 5 | import org.springframework.context.ApplicationContextAware; 6 | import org.springframework.stereotype.Component; 7 | 8 | /** 9 | * Spring容器工具类 10 | * 11 | * @author liangbaiakai 12 | * @version 0.1.0 13 | * @date 2020/5/15 18:17 14 | */ 15 | @Component 16 | public class SpringContextHolder implements ApplicationContextAware { 17 | 18 | private static ApplicationContext applicationContext; 19 | 20 | @Override 21 | public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { 22 | SpringContextHolder.applicationContext = applicationContext; 23 | } 24 | 25 | public static ApplicationContext getApplicationContext() { 26 | assertApplicationContext(null, null); 27 | return applicationContext; 28 | } 29 | 30 | @SuppressWarnings("unchecked") 31 | public static T getBean(String beanName) { 32 | assertApplicationContext(beanName, null); 33 | return (T) applicationContext.getBean(beanName); 34 | } 35 | 36 | public static T getBean(Class requiredType) { 37 | assertApplicationContext(null, requiredType); 38 | return applicationContext.getBean(requiredType); 39 | } 40 | 41 | private static void assertApplicationContext(String beanName, Class requiredType) { 42 | if (SpringContextHolder.applicationContext == null) { 43 | throw new RuntimeException("ApplicaitonContext property is NULL, please check whether SpringContextHolder is injected!"); 44 | } 45 | if (beanName != null) { 46 | boolean have = applicationContext.containsBean(beanName); 47 | if (!have) { 48 | throw new RuntimeException("This bean is not managed by the Spring container "); 49 | } 50 | } 51 | if (requiredType != null) { 52 | String[] beanNamesForType = applicationContext.getBeanNamesForType(requiredType); 53 | if (beanNamesForType == null || beanNamesForType.length == 0) { 54 | throw new RuntimeException("This bean is not managed by the Spring container "); 55 | } 56 | if (beanNamesForType.length != 1) { 57 | throw new RuntimeException("Class type fetching is not supported for multiple types of beans. Use the name to get this bean"); 58 | } 59 | boolean have = applicationContext.containsBean(beanNamesForType[0]); 60 | if (!have) { 61 | throw new RuntimeException("This bean is not managed by the Spring container "); 62 | } 63 | } 64 | 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/spring.factories: -------------------------------------------------------------------------------- 1 | # Auto Configure 2 | org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ 3 | com.github.liangbaika.validate.config.SpringValidateAutoConfig -------------------------------------------------------------------------------- /src/test/java/com/github/liangbaika/validate/test/Tests.java: -------------------------------------------------------------------------------- 1 | package com.github.liangbaika.validate.test; 2 | 3 | import com.github.liangbaika.validate.core.ValidateBuilder; 4 | import org.junit.Test; 5 | 6 | import static com.github.liangbaika.validate.enums.Check.*; 7 | 8 | /** 9 | * 测试 10 | * 11 | * @author lq 12 | * @version 1.0 13 | * @date 2021/1/29 21:44 14 | */ 15 | public class Tests { 16 | 17 | @Test 18 | public void testValidateBuilder() { 19 | ValidateBuilder validateBuilder = ValidateBuilder.build(); 20 | int failedCounts = validateBuilder 21 | .vali(ne, "3", "3", "不能等于") 22 | .vali(Chinese, "测试中文") 23 | .vali(isBirthdaystr, "1992-12-09") 24 | .vali(isUrl, "https://baidu.com") 25 | .doCheck() 26 | .getFailedCounts(); 27 | assert failedCounts == 1; 28 | assert validateBuilder.getSuccedCounts() == 3; 29 | 30 | } 31 | 32 | } 33 | --------------------------------------------------------------------------------