├── .gitignore ├── README.md ├── pom.xml └── src ├── main ├── java │ └── com │ │ └── github │ │ └── chentianming11 │ │ └── spring │ │ └── validation │ │ ├── SpringValidationApplication.java │ │ ├── base │ │ ├── BusinessCode.java │ │ ├── CommonExceptionHandler.java │ │ ├── EnvironmentConfig.java │ │ ├── Result.java │ │ ├── ReturnCode.java │ │ ├── Swagger2Config.java │ │ └── Validation │ │ │ ├── EncryptId.java │ │ │ ├── EncryptIdValidator.java │ │ │ └── ValidationList.java │ │ ├── controller │ │ ├── DegradeController.java │ │ ├── PersonController.java │ │ └── UserController.java │ │ ├── http │ │ ├── HttpDegradeApi.java │ │ ├── HttpDegradeFallback.java │ │ ├── HttpDegradeFallbackFactory.java │ │ └── HttpTestAPI.java │ │ └── pojo │ │ ├── PersonVO.java │ │ ├── SavePersonDTO.java │ │ ├── UpdatePersonDTO.java │ │ └── dto │ │ └── UserDTO.java └── resources │ ├── application-local.yml │ └── application.yml └── test └── java └── com └── github └── chentianming11 └── spring └── validation ├── DegradeTest.java └── SpringValidationApplicationTests.java /.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | !**/src/main/**/target/ 5 | !**/src/test/**/target/ 6 | 7 | ### STS ### 8 | .apt_generated 9 | .classpath 10 | .factorypath 11 | .project 12 | .settings 13 | .springBeans 14 | .sts4-cache 15 | 16 | ### IntelliJ IDEA ### 17 | .idea 18 | *.iws 19 | *.iml 20 | *.ipr 21 | 22 | ### NetBeans ### 23 | /nbproject/private/ 24 | /nbbuild/ 25 | /dist/ 26 | /nbdist/ 27 | /.nb-gradle/ 28 | build/ 29 | !**/src/main/**/build/ 30 | !**/src/test/**/build/ 31 | 32 | ### VS Code ### 33 | .vscode/ 34 | 35 | ### maven ### 36 | mvnw 37 | mvnw.cmd 38 | .mvn 39 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # spring-Validation 2 | 彻底搞懂spring-Validation 3 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.springframework.boot 7 | spring-boot-starter-parent 8 | 2.2.0.RELEASE 9 | 10 | 11 | com.github.chentianming11 12 | spring-validation 13 | 0.0.1-SNAPSHOT 14 | spring-validation 15 | 彻底搞懂spring-validation 16 | 17 | 18 | 1.8 19 | 20 | 21 | 22 | 23 | org.projectlombok 24 | lombok 25 | 1.18.6 26 | 27 | 28 | com.github.lianjiatech 29 | retrofit-spring-boot-starter 30 | 2.2.1 31 | 32 | 33 | org.springframework.boot 34 | spring-boot-starter 35 | 36 | 37 | 38 | org.springframework.boot 39 | spring-boot-starter-test 40 | test 41 | 42 | 43 | 44 | org.springframework.boot 45 | spring-boot-starter-validation 46 | 47 | 48 | 49 | com.alibaba.csp 50 | sentinel-core 51 | 1.6.3 52 | 53 | 54 | 55 | org.springframework.boot 56 | spring-boot-starter-web 57 | 58 | 59 | com.github.xiaoymin 60 | knife4j-spring-boot-starter 61 | 62 | 3.0.2 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | org.springframework.boot 71 | spring-boot-maven-plugin 72 | 73 | 74 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /src/main/java/com/github/chentianming11/spring/validation/SpringValidationApplication.java: -------------------------------------------------------------------------------- 1 | package com.github.chentianming11.spring.validation; 2 | 3 | import org.hibernate.validator.HibernateValidator; 4 | import org.springframework.boot.SpringApplication; 5 | import org.springframework.boot.autoconfigure.SpringBootApplication; 6 | import org.springframework.context.annotation.Bean; 7 | 8 | import javax.validation.Validation; 9 | import javax.validation.Validator; 10 | import javax.validation.ValidatorFactory; 11 | 12 | @SpringBootApplication 13 | public class SpringValidationApplication { 14 | 15 | // test代码 t 16 | 17 | public static void main(String[] args) { 18 | SpringApplication.run(SpringValidationApplication.class, args); 19 | } 20 | 21 | 22 | @Bean 23 | public Validator validator() { 24 | ValidatorFactory validatorFactory = Validation.byProvider(HibernateValidator.class) 25 | .configure() 26 | // 快速失败模式 27 | .failFast(true) 28 | .buildValidatorFactory(); 29 | return validatorFactory.getValidator(); 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/com/github/chentianming11/spring/validation/base/BusinessCode.java: -------------------------------------------------------------------------------- 1 | package com.github.chentianming11.spring.validation.base; 2 | 3 | /** 4 | * @author 陈添明 5 | * @date 2019/3/5 6 | */ 7 | public enum BusinessCode implements ReturnCode { 8 | 9 | 10 | 成功(0, "success"), 11 | 未知系统错误(10000, "未知系统错误"), 12 | 缺少参数(10001, "缺少参数"), 13 | 参数类型不匹配(10002, "参数类型不匹配"), 14 | 文件上传错误(10003, "文件上传错误"), 15 | 参数校验失败(10004, "参数校验失败"), 16 | 未知业务失败(20000, "未知业务失败"); 17 | 18 | /** 19 | * 业务编号 20 | */ 21 | private int code; 22 | 23 | /** 24 | * 业务值 25 | */ 26 | private String message; 27 | 28 | BusinessCode(int code, String message) { 29 | this.code = code; 30 | this.message = message; 31 | } 32 | 33 | /** 34 | * 获取返回编码 35 | * 36 | * @return code 37 | */ 38 | @Override 39 | public int code() { 40 | return code; 41 | } 42 | 43 | /** 44 | * 获取返回描述信息 45 | * 46 | * @return message 47 | */ 48 | @Override 49 | public String message() { 50 | return message; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/com/github/chentianming11/spring/validation/base/CommonExceptionHandler.java: -------------------------------------------------------------------------------- 1 | package com.github.chentianming11.spring.validation.base; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.springframework.beans.NotReadablePropertyException; 5 | import org.springframework.http.HttpStatus; 6 | import org.springframework.validation.BindingResult; 7 | import org.springframework.validation.FieldError; 8 | import org.springframework.web.bind.MethodArgumentNotValidException; 9 | import org.springframework.web.bind.annotation.ExceptionHandler; 10 | import org.springframework.web.bind.annotation.ResponseBody; 11 | import org.springframework.web.bind.annotation.ResponseStatus; 12 | import org.springframework.web.bind.annotation.RestControllerAdvice; 13 | 14 | import javax.validation.ConstraintViolationException; 15 | 16 | /** 17 | * @author 陈添明 18 | */ 19 | @RestControllerAdvice 20 | @Slf4j 21 | public class CommonExceptionHandler { 22 | 23 | @ExceptionHandler({MethodArgumentNotValidException.class}) 24 | @ResponseStatus(HttpStatus.OK) 25 | @ResponseBody 26 | public Result handleMethodArgumentNotValidException(MethodArgumentNotValidException ex) { 27 | BindingResult bindingResult = ex.getBindingResult(); 28 | StringBuilder sb = new StringBuilder("校验失败:"); 29 | for (FieldError fieldError : bindingResult.getFieldErrors()) { 30 | sb.append(fieldError.getField()).append(":").append(fieldError.getDefaultMessage()).append(", "); 31 | } 32 | String msg = sb.toString(); 33 | return Result.fail(BusinessCode.参数校验失败, msg); 34 | } 35 | 36 | @ExceptionHandler({ConstraintViolationException.class}) 37 | @ResponseStatus(HttpStatus.OK) 38 | @ResponseBody 39 | public Result handleConstraintViolationException(ConstraintViolationException ex) { 40 | return Result.fail(BusinessCode.参数校验失败, ex.getMessage()); 41 | } 42 | 43 | @ExceptionHandler({NotReadablePropertyException.class}) 44 | @ResponseStatus(HttpStatus.OK) 45 | @ResponseBody 46 | public Result handleNotReadablePropertyException(NotReadablePropertyException ex) { 47 | return Result.fail(BusinessCode.参数校验失败, ex.getMessage()); 48 | } 49 | 50 | 51 | @ExceptionHandler({Exception.class}) 52 | @ResponseStatus(HttpStatus.OK) 53 | @ResponseBody 54 | public Result handleException(Exception ex) { 55 | log.error("未知系统错误", ex); 56 | return Result.fail(BusinessCode.未知系统错误, ex.getMessage()); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/com/github/chentianming11/spring/validation/base/EnvironmentConfig.java: -------------------------------------------------------------------------------- 1 | package com.github.chentianming11.spring.validation.base; 2 | 3 | import org.springframework.context.EnvironmentAware; 4 | import org.springframework.context.annotation.Configuration; 5 | import org.springframework.core.env.Environment; 6 | 7 | /** 8 | * @author 陈添明 9 | */ 10 | @Configuration 11 | public class EnvironmentConfig implements EnvironmentAware { 12 | /** 13 | * Set the {@code Environment} that this component runs in. 14 | * 15 | * @param environment 16 | */ 17 | @Override 18 | public void setEnvironment(Environment environment) { 19 | System.out.println(environment); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/com/github/chentianming11/spring/validation/base/Result.java: -------------------------------------------------------------------------------- 1 | package com.github.chentianming11.spring.validation.base; 2 | 3 | import lombok.Data; 4 | import lombok.experimental.Accessors; 5 | 6 | import java.io.Serializable; 7 | 8 | /** 9 | * @author 陈添明 10 | * @date 2019/3/5 11 | */ 12 | @Data 13 | @Accessors(chain = true) 14 | public class Result implements Serializable { 15 | 16 | private static final long serialVersionUID = -504027247149928390L; 17 | 18 | private int code; 19 | private String msg; 20 | private String exceptionMsg; 21 | private T body; 22 | 23 | public static Result ok(T body) { 24 | return new Result<>() 25 | .setBody(body) 26 | .setCode(BusinessCode.成功.code()) 27 | .setMsg(BusinessCode.成功.message()); 28 | } 29 | 30 | public static Result ok() { 31 | return new Result<>() 32 | .setCode(BusinessCode.成功.code()) 33 | .setMsg(BusinessCode.成功.message()); 34 | } 35 | 36 | public static Result fail(ReturnCode returnCode) { 37 | return new Result<>() 38 | .setCode(returnCode.code()) 39 | .setMsg(returnCode.message()); 40 | } 41 | 42 | public static Result fail(ReturnCode returnCode, String exceptionMsg) { 43 | return new Result<>() 44 | .setCode(returnCode.code()) 45 | .setMsg(returnCode.message()) 46 | .setExceptionMsg(exceptionMsg); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/com/github/chentianming11/spring/validation/base/ReturnCode.java: -------------------------------------------------------------------------------- 1 | package com.github.chentianming11.spring.validation.base; 2 | 3 | /** 4 | * 如果需要自定义错误编码,需要继承ReturnCode接口 5 | */ 6 | public interface ReturnCode { 7 | 8 | /** 9 | * 获取返回编码 10 | * @return code 11 | */ 12 | int code(); 13 | 14 | /** 15 | * 获取返回描述信息 16 | * @return message 17 | */ 18 | String message(); 19 | 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/com/github/chentianming11/spring/validation/base/Swagger2Config.java: -------------------------------------------------------------------------------- 1 | //package com.github.chentianming11.spring.validation.base; 2 | // 3 | //import org.springframework.context.annotation.Bean; 4 | //import org.springframework.context.annotation.Configuration; 5 | //import springfox.documentation.builders.ApiInfoBuilder; 6 | //import springfox.documentation.builders.PathSelectors; 7 | //import springfox.documentation.builders.RequestHandlerSelectors; 8 | //import springfox.documentation.spi.DocumentationType; 9 | //import springfox.documentation.spring.web.plugins.Docket; 10 | //import springfox.documentation.swagger2.annotations.EnableSwagger2; 11 | // 12 | //@Configuration 13 | //@EnableSwagger2 14 | //public class Swagger2Config { 15 | // 16 | // 17 | // @Bean(value = "defaultApi2") 18 | // public Docket defaultApi2() { 19 | // Docket docket=new Docket(DocumentationType.SWAGGER_2) 20 | // .apiInfo(new ApiInfoBuilder() 21 | // .title("交易助手后台接口文档") 22 | // .description("# helicarrier RESTful APIs") 23 | // .termsOfServiceUrl("http://dora.shtest.ke.com/") 24 | // .version("1.0") 25 | // .build()) 26 | // //分组名称 27 | // .select() 28 | // //这里指定Controller扫描包路径 29 | // .apis(RequestHandlerSelectors.basePackage("com.github.chentianming11.spring.validation")) 30 | // .paths(PathSelectors.any()) 31 | // .build(); 32 | // return docket; 33 | // } 34 | //} -------------------------------------------------------------------------------- /src/main/java/com/github/chentianming11/spring/validation/base/Validation/EncryptId.java: -------------------------------------------------------------------------------- 1 | package com.github.chentianming11.spring.validation.base.Validation; 2 | 3 | import javax.validation.Constraint; 4 | import javax.validation.Payload; 5 | import java.lang.annotation.Documented; 6 | import java.lang.annotation.Retention; 7 | import java.lang.annotation.Target; 8 | 9 | import static java.lang.annotation.ElementType.*; 10 | import static java.lang.annotation.RetentionPolicy.RUNTIME; 11 | 12 | @Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER}) 13 | @Retention(RUNTIME) 14 | @Documented 15 | @Constraint(validatedBy = {EncryptIdValidator.class}) 16 | public @interface EncryptId { 17 | 18 | // 默认错误消息 19 | String message() default "加密id格式错误"; 20 | 21 | // 分组 22 | Class[] groups() default {}; 23 | 24 | // 负载 25 | Class[] payload() default {}; 26 | } -------------------------------------------------------------------------------- /src/main/java/com/github/chentianming11/spring/validation/base/Validation/EncryptIdValidator.java: -------------------------------------------------------------------------------- 1 | package com.github.chentianming11.spring.validation.base.Validation; 2 | 3 | import javax.validation.ConstraintValidator; 4 | import javax.validation.ConstraintValidatorContext; 5 | import java.util.regex.Matcher; 6 | import java.util.regex.Pattern; 7 | 8 | public class EncryptIdValidator implements ConstraintValidator { 9 | 10 | private static final Pattern PATTERN = Pattern.compile("^[a-f\\d]{32,256}$"); 11 | 12 | @Override 13 | public boolean isValid(String value, ConstraintValidatorContext context) { 14 | // 不为null才进行校验 15 | if (value != null) { 16 | Matcher matcher = PATTERN.matcher(value); 17 | return matcher.find(); 18 | } 19 | return true; 20 | } 21 | } -------------------------------------------------------------------------------- /src/main/java/com/github/chentianming11/spring/validation/base/Validation/ValidationList.java: -------------------------------------------------------------------------------- 1 | package com.github.chentianming11.spring.validation.base.Validation; 2 | 3 | import lombok.experimental.Delegate; 4 | 5 | import javax.validation.Valid; 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | 9 | public class ValidationList implements List { 10 | 11 | @Delegate 12 | @Valid // 一定要加@Valid注解 13 | public List list = new ArrayList<>(); 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/com/github/chentianming11/spring/validation/controller/DegradeController.java: -------------------------------------------------------------------------------- 1 | package com.github.chentianming11.spring.validation.controller; 2 | 3 | import com.github.chentianming11.spring.validation.base.Result; 4 | import com.github.chentianming11.spring.validation.http.HttpDegradeApi; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.web.bind.annotation.GetMapping; 7 | import org.springframework.web.bind.annotation.RequestMapping; 8 | import org.springframework.web.bind.annotation.RestController; 9 | 10 | import java.util.Random; 11 | 12 | /** 13 | * @author 陈添明 14 | */ 15 | @RequestMapping("/api/degrade") 16 | @RestController 17 | public class DegradeController { 18 | 19 | @Autowired 20 | HttpDegradeApi httpDegradeApi; 21 | 22 | 23 | @GetMapping("/test") 24 | public Result test() throws InterruptedException { 25 | Random random = new Random(System.currentTimeMillis()); 26 | int i = random.nextInt(1_000); 27 | Thread.sleep(i); 28 | return Result.ok(i); 29 | } 30 | 31 | 32 | @GetMapping("/test2") 33 | public Result test2() throws InterruptedException { 34 | Random random = new Random(System.currentTimeMillis()); 35 | int i = random.nextInt(2_000); 36 | Thread.sleep(i); 37 | return Result.ok(i); 38 | } 39 | 40 | @GetMapping("/test3") 41 | public Result test3() throws InterruptedException { 42 | Random random = new Random(System.currentTimeMillis()); 43 | int i = random.nextInt(3_000); 44 | Thread.sleep(i); 45 | return Result.ok(i); 46 | } 47 | 48 | 49 | @GetMapping("/test/execute") 50 | public Result testExecute() throws InterruptedException { 51 | Result test = httpDegradeApi.test(); 52 | return Result.ok(test); 53 | } 54 | 55 | @GetMapping("/test2/execute") 56 | public Result test2Execute() throws InterruptedException { 57 | Result integerResult = httpDegradeApi.test2(); 58 | return Result.ok(integerResult); 59 | } 60 | 61 | @GetMapping("/test3/execute") 62 | public Result test3Execute() throws InterruptedException { 63 | Result integerResult = httpDegradeApi.test3(); 64 | return Result.ok(integerResult); 65 | } 66 | 67 | 68 | 69 | } 70 | -------------------------------------------------------------------------------- /src/main/java/com/github/chentianming11/spring/validation/controller/PersonController.java: -------------------------------------------------------------------------------- 1 | package com.github.chentianming11.spring.validation.controller; 2 | 3 | import com.github.chentianming11.spring.validation.base.Result; 4 | import com.github.chentianming11.spring.validation.pojo.PersonVO; 5 | import com.github.chentianming11.spring.validation.pojo.SavePersonDTO; 6 | import com.github.chentianming11.spring.validation.pojo.UpdatePersonDTO; 7 | import io.swagger.annotations.Api; 8 | import io.swagger.annotations.ApiOperation; 9 | import io.swagger.annotations.ApiParam; 10 | import org.springframework.validation.annotation.Validated; 11 | import org.springframework.web.bind.annotation.*; 12 | 13 | import javax.validation.Valid; 14 | import javax.validation.constraints.*; 15 | import java.util.ArrayList; 16 | import java.util.List; 17 | 18 | /** 19 | * @author 陈添明 20 | */ 21 | @RestController 22 | @RequestMapping("/api/person") 23 | @Validated 24 | @Api(tags = "Person管理") 25 | public class PersonController { 26 | 27 | 28 | @ApiOperation("保存用户") 29 | @PostMapping("savePerson") 30 | public Result savePerson(@RequestBody @Valid SavePersonDTO person) { 31 | PersonVO personVO = new PersonVO().setAge(10).setEmail("xxxxx").setId(1L).setName("哈哈"); 32 | return Result.ok(personVO); 33 | } 34 | 35 | @ApiOperation("更新用户") 36 | @PostMapping("updatePerson") 37 | public Result updatePerson(@RequestBody @Valid UpdatePersonDTO person) { 38 | PersonVO personVO = new PersonVO().setAge(10).setEmail("xxxxx").setId(1L).setName("哈哈"); 39 | return Result.ok(personVO); 40 | } 41 | 42 | 43 | @ApiOperation("查询person") 44 | @GetMapping("queryPerson") 45 | public Result> queryPerson( 46 | @ApiParam("用户id") @Min(1000) @Max(10000000) Long id, 47 | @ApiParam("姓名") @NotNull @Size(min = 2, max = 10) String name, 48 | @ApiParam("年龄") @NotNull @Max(200) Integer age, 49 | @ApiParam("邮箱") @Pattern(regexp = "^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\\.[a-zA-Z0-9_-]+)+$") String email) { 50 | List list = new ArrayList<>(); 51 | list.add(new PersonVO().setAge(10).setEmail("xxxxx").setId(1L).setName("哈哈")); 52 | return Result.ok(list); 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/com/github/chentianming11/spring/validation/controller/UserController.java: -------------------------------------------------------------------------------- 1 | package com.github.chentianming11.spring.validation.controller; 2 | 3 | import com.github.chentianming11.spring.validation.base.Result; 4 | import com.github.chentianming11.spring.validation.base.Validation.ValidationList; 5 | import com.github.chentianming11.spring.validation.http.HttpTestAPI; 6 | import com.github.chentianming11.spring.validation.pojo.dto.UserDTO; 7 | import io.swagger.annotations.Api; 8 | import io.swagger.annotations.ApiOperation; 9 | import io.swagger.annotations.ApiParam; 10 | import org.hibernate.validator.constraints.Length; 11 | import org.springframework.beans.factory.annotation.Autowired; 12 | import org.springframework.validation.annotation.Validated; 13 | import org.springframework.web.bind.annotation.*; 14 | 15 | import javax.validation.ConstraintViolation; 16 | import javax.validation.Valid; 17 | import javax.validation.constraints.Min; 18 | import javax.validation.constraints.NotNull; 19 | import java.util.Set; 20 | 21 | /** 22 | * @author 陈添明 23 | */ 24 | @RequestMapping("/api/user") 25 | @RestController 26 | @Validated 27 | @Api("用户管理") 28 | public class UserController { 29 | 30 | @Autowired 31 | private javax.validation.Validator globalValidator; 32 | 33 | @Autowired 34 | private HttpTestAPI httpTestAPI; 35 | 36 | // 编程式校验 37 | @PostMapping("/saveWithCodingValidate") 38 | @ApiOperation("编程式校验保存") 39 | public Result saveWithCodingValidate(@RequestBody @Valid UserDTO userDTO) { 40 | Set> validate = globalValidator.validate(userDTO, UserDTO.Save.class); 41 | // 如果校验通过,validate为空;否则,validate包含未校验通过项 42 | if (validate.isEmpty()) { 43 | // 校验通过,才会执行业务逻辑处理 44 | 45 | } else { 46 | for (ConstraintViolation userDTOConstraintViolation : validate) { 47 | // 校验失败,做其它逻辑 48 | System.out.println(userDTOConstraintViolation); 49 | } 50 | } 51 | return Result.ok(); 52 | } 53 | 54 | @PostMapping("/save") 55 | @ApiOperation("保存用户") 56 | public Result saveUser(@RequestBody @Validated(UserDTO.Save.class) UserDTO userDTO) { 57 | // 校验通过,才会执行业务逻辑处理 58 | return Result.ok(); 59 | } 60 | 61 | @PostMapping("/saveList") 62 | @ApiOperation("批量保存") 63 | public Result saveList(@RequestBody @Validated(UserDTO.Save.class) ValidationList userList) { 64 | // 校验通过,才会执行业务逻辑处理 65 | return Result.ok(); 66 | } 67 | 68 | @PostMapping("/update") 69 | @ApiOperation("更新用户信息") 70 | public Result updateUser(@RequestBody @Validated(UserDTO.Update.class) UserDTO userDTO) { 71 | // 校验通过,才会执行业务逻辑处理 72 | return Result.ok(); 73 | } 74 | 75 | 76 | // 路径变量 77 | @GetMapping("{userId}") 78 | @ApiOperation("根据userId查询用户信息") 79 | public Result detail(@PathVariable("userId") @Min(10000000000000000L) @ApiParam("用户id") Long userId) { 80 | // 校验通过,才会执行业务逻辑处理 81 | UserDTO userDTO = new UserDTO(); 82 | userDTO.setUserId(userId); 83 | userDTO.setAccount("11111111111111111"); 84 | userDTO.setUserName("xixi"); 85 | userDTO.setAccount("11111111111111111"); 86 | return Result.ok(userDTO); 87 | } 88 | 89 | // 查询参数 90 | @GetMapping("getByAccount") 91 | @ApiOperation("根据account查询用户信息") 92 | public Result getByAccount(@Length(min = 6, max = 20) @NotNull @ApiParam("账号") String account) { 93 | // 校验通过,才会执行业务逻辑处理 94 | UserDTO userDTO = new UserDTO(); 95 | userDTO.setUserId(10000000000000003L); 96 | userDTO.setAccount(account); 97 | userDTO.setUserName("xixi"); 98 | userDTO.setAccount("11111111111111111"); 99 | return Result.ok(userDTO); 100 | } 101 | 102 | @GetMapping("/httpTest") 103 | public Result httpTest() { 104 | Result account123 = httpTestAPI.getByAccount("account123"); 105 | System.out.println(account123); 106 | return Result.ok(account123); 107 | } 108 | 109 | @GetMapping("/throw404Exception") 110 | public Result throw404Exception() { 111 | Result account123 = null; 112 | try { 113 | account123 = httpTestAPI.throw404Exception("account123"); 114 | } catch (Throwable e) { 115 | while (true) { 116 | if (e != null) { 117 | System.out.println(e.getClass()); 118 | e = e.getCause(); 119 | } 120 | } 121 | } 122 | return Result.ok(account123); 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /src/main/java/com/github/chentianming11/spring/validation/http/HttpDegradeApi.java: -------------------------------------------------------------------------------- 1 | package com.github.chentianming11.spring.validation.http; 2 | 3 | import com.github.chentianming11.spring.validation.base.Result; 4 | import com.github.lianjiatech.retrofit.spring.boot.annotation.RetrofitClient; 5 | import com.github.lianjiatech.retrofit.spring.boot.degrade.Degrade; 6 | import retrofit2.http.GET; 7 | 8 | /** 9 | * @author 陈添明 10 | */ 11 | @RetrofitClient(baseUrl = "http://localhost:8080/api/degrade", fallbackFactory = HttpDegradeFallbackFactory.class) 12 | @Degrade(count = 300, timeWindow = 3) 13 | public interface HttpDegradeApi { 14 | 15 | 16 | @GET("test") 17 | @Degrade(count = 500, timeWindow = 3) 18 | Result test(); 19 | 20 | @GET("test2") 21 | @Degrade(count = 500, timeWindow = 3) 22 | Result test2(); 23 | 24 | @GET("test3") 25 | Result test3(); 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/github/chentianming11/spring/validation/http/HttpDegradeFallback.java: -------------------------------------------------------------------------------- 1 | package com.github.chentianming11.spring.validation.http; 2 | 3 | import com.github.chentianming11.spring.validation.base.Result; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.springframework.stereotype.Service; 6 | 7 | /** 8 | * @author 陈添明 9 | */ 10 | @Slf4j 11 | @Service 12 | public class HttpDegradeFallback implements HttpDegradeApi { 13 | 14 | 15 | @Override 16 | public Result test() { 17 | Result fallback = new Result<>(); 18 | fallback.setCode(100) 19 | .setMsg("fallback") 20 | .setBody(1000000); 21 | return fallback; 22 | } 23 | 24 | @Override 25 | public Result test2() { 26 | Result fallback = new Result<>(); 27 | fallback.setCode(100) 28 | .setMsg("fallback") 29 | .setBody(1000000); 30 | return fallback; 31 | } 32 | 33 | @Override 34 | public Result test3() { 35 | Result fallback = new Result<>(); 36 | fallback.setCode(100) 37 | .setMsg("fallback") 38 | .setBody(1000000); 39 | return fallback; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/com/github/chentianming11/spring/validation/http/HttpDegradeFallbackFactory.java: -------------------------------------------------------------------------------- 1 | package com.github.chentianming11.spring.validation.http; 2 | 3 | import com.github.chentianming11.spring.validation.base.Result; 4 | import com.github.lianjiatech.retrofit.spring.boot.degrade.FallbackFactory; 5 | import lombok.extern.slf4j.Slf4j; 6 | import org.springframework.stereotype.Service; 7 | 8 | /** 9 | * @author 陈添明 10 | */ 11 | @Slf4j 12 | @Service 13 | public class HttpDegradeFallbackFactory implements FallbackFactory { 14 | 15 | /** 16 | * Returns an instance of the fallback appropriate for the given cause 17 | * 18 | * @param cause fallback cause 19 | * @return 实现了retrofit接口的实例。an instance that implements the retrofit interface. 20 | */ 21 | @Override 22 | public HttpDegradeApi create(Throwable cause) { 23 | 24 | log.error("触发熔断了! ", cause.getMessage(), cause); 25 | 26 | return new HttpDegradeApi() { 27 | @Override 28 | public Result test() { 29 | Result fallback = new Result<>(); 30 | fallback.setCode(100) 31 | .setMsg("fallback") 32 | .setBody(1000000); 33 | return fallback; 34 | } 35 | 36 | @Override 37 | public Result test2() { 38 | Result fallback = new Result<>(); 39 | fallback.setCode(100) 40 | .setMsg("fallback") 41 | .setBody(1000000); 42 | return fallback; 43 | } 44 | 45 | @Override 46 | public Result test3() { 47 | Result fallback = new Result<>(); 48 | fallback.setCode(100) 49 | .setMsg("fallback") 50 | .setBody(1000000); 51 | return fallback; 52 | } 53 | }; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/com/github/chentianming11/spring/validation/http/HttpTestAPI.java: -------------------------------------------------------------------------------- 1 | package com.github.chentianming11.spring.validation.http; 2 | 3 | import com.github.chentianming11.spring.validation.base.Result; 4 | import com.github.chentianming11.spring.validation.pojo.dto.UserDTO; 5 | import com.github.lianjiatech.retrofit.spring.boot.annotation.RetrofitClient; 6 | import retrofit2.http.GET; 7 | import retrofit2.http.Query; 8 | 9 | /** 10 | * @author 陈添明 11 | */ 12 | @RetrofitClient(baseUrl = "http://localhost:8080/api/user") 13 | public interface HttpTestAPI { 14 | 15 | @GET("getByAccount") 16 | Result getByAccount(@Query("account") String account); 17 | 18 | @GET("getByAccount/notExist") 19 | Result throw404Exception(@Query("account") String account); 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/com/github/chentianming11/spring/validation/pojo/PersonVO.java: -------------------------------------------------------------------------------- 1 | package com.github.chentianming11.spring.validation.pojo; 2 | 3 | import io.swagger.annotations.ApiModel; 4 | import io.swagger.annotations.ApiModelProperty; 5 | import lombok.Data; 6 | import lombok.experimental.Accessors; 7 | 8 | /** 9 | * @author 陈添明 10 | */ 11 | 12 | @Data 13 | @Accessors(chain = true) 14 | @ApiModel("用户VO类") 15 | public class PersonVO { 16 | 17 | @ApiModelProperty("用户id") 18 | private Long id; 19 | 20 | @ApiModelProperty("姓名") 21 | private String name; 22 | 23 | @ApiModelProperty("年龄") 24 | private Integer age; 25 | 26 | @ApiModelProperty("邮箱") 27 | private String email; 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/com/github/chentianming11/spring/validation/pojo/SavePersonDTO.java: -------------------------------------------------------------------------------- 1 | package com.github.chentianming11.spring.validation.pojo; 2 | 3 | import io.swagger.annotations.ApiModel; 4 | import io.swagger.annotations.ApiModelProperty; 5 | import lombok.Data; 6 | import lombok.experimental.Accessors; 7 | 8 | import javax.validation.constraints.Max; 9 | import javax.validation.constraints.NotNull; 10 | import javax.validation.constraints.Pattern; 11 | import javax.validation.constraints.Size; 12 | 13 | /** 14 | * @author 陈添明 15 | */ 16 | @Data 17 | @Accessors(chain = true) 18 | @ApiModel("保存用户DTO类") 19 | public class SavePersonDTO { 20 | 21 | @ApiModelProperty("姓名") 22 | @NotNull 23 | @Size(min = 2, max = 10) 24 | private String name; 25 | 26 | @ApiModelProperty("年龄") 27 | @NotNull 28 | @Max(200) 29 | private Integer age; 30 | 31 | @ApiModelProperty("邮箱") 32 | @Pattern(regexp = "^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\\.[a-zA-Z0-9_-]+)+$") 33 | private String email; 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/com/github/chentianming11/spring/validation/pojo/UpdatePersonDTO.java: -------------------------------------------------------------------------------- 1 | package com.github.chentianming11.spring.validation.pojo; 2 | 3 | import io.swagger.annotations.ApiModel; 4 | import io.swagger.annotations.ApiModelProperty; 5 | import lombok.Data; 6 | import lombok.experimental.Accessors; 7 | 8 | import javax.validation.constraints.Max; 9 | import javax.validation.constraints.NotNull; 10 | import javax.validation.constraints.Pattern; 11 | import javax.validation.constraints.Size; 12 | 13 | /** 14 | * @author 陈添明 15 | */ 16 | @Data 17 | @Accessors(chain = true) 18 | @ApiModel("更新用户DTO类") 19 | public class UpdatePersonDTO { 20 | 21 | 22 | @ApiModelProperty("用户id") 23 | @NotNull 24 | private Long id; 25 | 26 | @ApiModelProperty("姓名") 27 | @Size(min = 2, max = 10) 28 | private String name; 29 | 30 | @ApiModelProperty("年龄") 31 | @Max(200) 32 | private Integer age; 33 | 34 | @ApiModelProperty("邮箱") 35 | @Pattern(regexp = "^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\\.[a-zA-Z0-9_-]+)+$") 36 | private String email; 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/com/github/chentianming11/spring/validation/pojo/dto/UserDTO.java: -------------------------------------------------------------------------------- 1 | package com.github.chentianming11.spring.validation.pojo.dto; 2 | 3 | import io.swagger.annotations.ApiModel; 4 | import io.swagger.annotations.ApiModelProperty; 5 | import lombok.Data; 6 | import org.hibernate.validator.constraints.Length; 7 | 8 | import javax.validation.Valid; 9 | import javax.validation.constraints.Min; 10 | import javax.validation.constraints.NotNull; 11 | 12 | /** 13 | * @author 陈添明 14 | */ 15 | @Data 16 | @ApiModel("用户实体类") 17 | public class UserDTO { 18 | 19 | @ApiModelProperty("用户id") 20 | @Min(value = 10000000000000000L, groups = Update.class) 21 | private Long userId; 22 | 23 | @ApiModelProperty("用户名") 24 | @NotNull(groups = {Save.class, Update.class}) 25 | @Length(min = 2, max = 10, groups = {Save.class, Update.class}) 26 | private String userName; 27 | 28 | @ApiModelProperty("账号") 29 | @NotNull(groups = {Save.class, Update.class}) 30 | @Length(min = 6, max = 20, groups = {Save.class, Update.class}) 31 | private String account; 32 | 33 | @ApiModelProperty("密码") 34 | @NotNull(groups = {Save.class, Update.class}) 35 | @Length(min = 6, max = 20, groups = {Save.class, Update.class}) 36 | private String password; 37 | 38 | @ApiModelProperty("工作") 39 | @NotNull(groups = {Save.class, Update.class}) 40 | @Valid 41 | private Job job; 42 | 43 | @Data 44 | @ApiModel("工作实体类") 45 | public static class Job { 46 | 47 | @ApiModelProperty("工作id") 48 | @Min(value = 1, groups = Update.class) 49 | private Long jobId; 50 | 51 | @ApiModelProperty("工作名称") 52 | @NotNull(groups = {Save.class, Update.class}) 53 | @Length(min = 2, max = 10, groups = {Save.class, Update.class}) 54 | private String jobName; 55 | 56 | @ApiModelProperty("岗位") 57 | @NotNull(groups = {Save.class, Update.class}) 58 | @Length(min = 2, max = 10, groups = {Save.class, Update.class}) 59 | private String position; 60 | } 61 | 62 | /** 63 | * 保存的时候校验分组 64 | */ 65 | public interface Save { 66 | } 67 | 68 | /** 69 | * 更新的时候校验分组 70 | */ 71 | public interface Update { 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/main/resources/application-local.yml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | test: 5 | local: local 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | retrofit: 2 | # \u542F\u7528\u65E5\u5FD7\u6253\u5370 3 | enable-log: true 4 | # \u8FDE\u63A5\u6C60\u914D\u7F6E 5 | pool: 6 | test1: 7 | max-idle-connections: 3 8 | keep-alive-second: 100 9 | test2: 10 | max-idle-connections: 5 11 | keep-alive-second: 50 12 | # \u7981\u7528void\u8FD4\u56DE\u503C\u7C7B\u578B 13 | disable-void-return-type: false 14 | # \u65E5\u5FD7\u6253\u5370\u62E6\u622A\u5668 15 | logging-interceptor: com.github.lianjiatech.retrofit.spring.boot.interceptor.DefaultLoggingInterceptor 16 | # \u8BF7\u6C42\u91CD\u8BD5\u62E6\u622A\u5668 17 | retry-interceptor: com.github.lianjiatech.retrofit.spring.boot.retry.DefaultRetryInterceptor 18 | # \u5168\u5C40\u8F6C\u6362\u5668\u5DE5\u5382 19 | global-converter-factories: 20 | - retrofit2.converter.jackson.JacksonConverterFactory 21 | # \u5168\u5C40\u8C03\u7528\u9002\u914D\u5668\u5DE5\u5382 22 | global-call-adapter-factories: 23 | - com.github.lianjiatech.retrofit.spring.boot.core.BodyCallAdapterFactory 24 | - com.github.lianjiatech.retrofit.spring.boot.core.ResponseCallAdapterFactory 25 | enable-degrade: true 26 | degrade-type: sentinel 27 | resource-name-parser: com.github.lianjiatech.retrofit.spring.boot.degrade.DefaultResourceNameParser 28 | 29 | 30 | #spring: 31 | # application: 32 | # name: jy-helicarrier-api 33 | # profiles: 34 | # active: local -------------------------------------------------------------------------------- /src/test/java/com/github/chentianming11/spring/validation/DegradeTest.java: -------------------------------------------------------------------------------- 1 | package com.github.chentianming11.spring.validation; 2 | 3 | import okhttp3.OkHttpClient; 4 | import okhttp3.Request; 5 | import okhttp3.Response; 6 | import org.junit.Test; 7 | 8 | import java.io.IOException; 9 | import java.util.concurrent.ExecutorService; 10 | import java.util.concurrent.Executors; 11 | 12 | /** 13 | * @author 陈添明 14 | */ 15 | public class DegradeTest { 16 | 17 | 18 | @Test 19 | public void test1() throws IOException, InterruptedException { 20 | OkHttpClient httpClient = new OkHttpClient.Builder() 21 | .build(); 22 | 23 | Request request = new Request.Builder() 24 | .url("http://api-dora.shoff.ke.com/keApi/test/http") 25 | .build(); 26 | 27 | ExecutorService executorService = Executors.newFixedThreadPool(100); 28 | 29 | executorService.execute(() -> { 30 | for (int i = 0; i < 50; i++) { 31 | Response response = null; 32 | try { 33 | response = httpClient.newCall(request).execute(); 34 | } catch (IOException e) { 35 | e.printStackTrace(); 36 | } 37 | String string = null; 38 | try { 39 | string = response.body().string(); 40 | } catch (IOException e) { 41 | e.printStackTrace(); 42 | } 43 | System.out.println(string); 44 | } 45 | }); 46 | 47 | executorService.execute(() -> { 48 | for (int i = 0; i < 50; i++) { 49 | Response response = null; 50 | try { 51 | response = httpClient.newCall(request).execute(); 52 | } catch (IOException e) { 53 | e.printStackTrace(); 54 | } 55 | String string = null; 56 | try { 57 | string = response.body().string(); 58 | } catch (IOException e) { 59 | e.printStackTrace(); 60 | } 61 | System.out.println(string); 62 | } 63 | }); 64 | 65 | executorService.execute(() -> { 66 | for (int i = 0; i < 50; i++) { 67 | Response response = null; 68 | try { 69 | response = httpClient.newCall(request).execute(); 70 | } catch (IOException e) { 71 | e.printStackTrace(); 72 | } 73 | String string = null; 74 | try { 75 | string = response.body().string(); 76 | } catch (IOException e) { 77 | e.printStackTrace(); 78 | } 79 | System.out.println(string); 80 | } 81 | }); 82 | 83 | executorService.execute(() -> { 84 | for (int i = 0; i < 50; i++) { 85 | Response response = null; 86 | try { 87 | response = httpClient.newCall(request).execute(); 88 | } catch (IOException e) { 89 | e.printStackTrace(); 90 | } 91 | String string = null; 92 | try { 93 | string = response.body().string(); 94 | } catch (IOException e) { 95 | e.printStackTrace(); 96 | } 97 | System.out.println(string); 98 | } 99 | }); 100 | 101 | 102 | executorService.execute(() -> { 103 | for (int i = 0; i < 50; i++) { 104 | Response response = null; 105 | try { 106 | response = httpClient.newCall(request).execute(); 107 | } catch (IOException e) { 108 | e.printStackTrace(); 109 | } 110 | String string = null; 111 | try { 112 | string = response.body().string(); 113 | } catch (IOException e) { 114 | e.printStackTrace(); 115 | } 116 | System.out.println(string); 117 | } 118 | }); 119 | 120 | 121 | executorService.execute(() -> { 122 | for (int i = 0; i < 50; i++) { 123 | Response response = null; 124 | try { 125 | response = httpClient.newCall(request).execute(); 126 | } catch (IOException e) { 127 | e.printStackTrace(); 128 | } 129 | String string = null; 130 | try { 131 | string = response.body().string(); 132 | } catch (IOException e) { 133 | e.printStackTrace(); 134 | } 135 | System.out.println(string); 136 | } 137 | }); 138 | 139 | executorService.execute(() -> { 140 | for (int i = 0; i < 50; i++) { 141 | Response response = null; 142 | try { 143 | response = httpClient.newCall(request).execute(); 144 | } catch (IOException e) { 145 | e.printStackTrace(); 146 | } 147 | String string = null; 148 | try { 149 | string = response.body().string(); 150 | } catch (IOException e) { 151 | e.printStackTrace(); 152 | } 153 | System.out.println(string); 154 | } 155 | }); 156 | 157 | executorService.execute(() -> { 158 | for (int i = 0; i < 50; i++) { 159 | Response response = null; 160 | try { 161 | response = httpClient.newCall(request).execute(); 162 | } catch (IOException e) { 163 | e.printStackTrace(); 164 | } 165 | String string = null; 166 | try { 167 | string = response.body().string(); 168 | } catch (IOException e) { 169 | e.printStackTrace(); 170 | } 171 | System.out.println(string); 172 | } 173 | }); 174 | 175 | 176 | executorService.execute(() -> { 177 | for (int i = 0; i < 50; i++) { 178 | Response response = null; 179 | try { 180 | response = httpClient.newCall(request).execute(); 181 | } catch (IOException e) { 182 | e.printStackTrace(); 183 | } 184 | String string = null; 185 | try { 186 | string = response.body().string(); 187 | } catch (IOException e) { 188 | e.printStackTrace(); 189 | } 190 | System.out.println(string); 191 | } 192 | }); 193 | 194 | 195 | executorService.execute(() -> { 196 | for (int i = 0; i < 50; i++) { 197 | Response response = null; 198 | try { 199 | response = httpClient.newCall(request).execute(); 200 | } catch (IOException e) { 201 | e.printStackTrace(); 202 | } 203 | String string = null; 204 | try { 205 | string = response.body().string(); 206 | } catch (IOException e) { 207 | e.printStackTrace(); 208 | } 209 | System.out.println(string); 210 | } 211 | }); 212 | 213 | executorService.execute(() -> { 214 | for (int i = 0; i < 50; i++) { 215 | Response response = null; 216 | try { 217 | response = httpClient.newCall(request).execute(); 218 | } catch (IOException e) { 219 | e.printStackTrace(); 220 | } 221 | String string = null; 222 | try { 223 | string = response.body().string(); 224 | } catch (IOException e) { 225 | e.printStackTrace(); 226 | } 227 | System.out.println(string); 228 | } 229 | }); 230 | 231 | 232 | 233 | 234 | Thread.currentThread().join(); 235 | 236 | 237 | } 238 | } 239 | -------------------------------------------------------------------------------- /src/test/java/com/github/chentianming11/spring/validation/SpringValidationApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.github.chentianming11.spring.validation; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | import org.springframework.test.context.junit4.SpringRunner; 7 | 8 | @RunWith(SpringRunner.class) 9 | @SpringBootTest 10 | public class SpringValidationApplicationTests { 11 | 12 | @Test 13 | public void contextLoads() { 14 | } 15 | 16 | } 17 | --------------------------------------------------------------------------------