当前位置:   article > 正文

SpringBoot学习_@schema(description

@schema(description

Spring缺点:

        1)配置繁琐

        2)依赖繁琐

SpringBoot:

        1)自动配置

                SpringBoot的自动配置是一个运行时(更准确的说,是应用程序启动时)的过程,考虑了众多因素,才决定Spring配置应该用哪个,不应该用哪个。该过程是SpringBoot自动完成的。

        2)起步依赖

                起步依赖本质上是一个Maven项目对象模型,定义了对其他库的传递依赖,这些东西加在一起即支持某项目功能

        3)辅助功能

                提供了一些大型项目中常见的非功能性特征,如嵌入式服务器、安全、指标、健康检查、外部配置。

*总结:SpringBoot提供了一种快速开发Spring项目的方式,而不是对Spring的功能上的增强。

SpringBootg工程搭建:

        1)maven方式创建

        2)下载插件 Alibaba Cloud Toolkit

                 下载慢可以配置代理:

JetBrains Marketplaceicon-default.png?t=N7T8https://plugins.jetbrains.com        3)Spring Initializr方式创建

SpringBoot起步依赖原理分析:

        1)spring-boot-starter-paren

  1. //继承父工程
  2. <parent>
  3. <groupId>org.springframework.boot</groupId>
  4. <artifactId>spring-boot-starter-parent</artifactId>
  5. <version>2.7.3</version>
  6. </parent>

        2)spring-boot-starter-web

  1. //导入起步依赖
  2. <dependency>
  3. <groupId>org.springframework.boot</groupId>
  4. <artifactId>spring-boot-starter-web</artifactId>
  5. </dependency>

        *总结

                1、在spring-boot-starter-parent中定义了各种技术的版本信息,组合了一套最优搭配的技术版本        

                2、在各种starter中,定义了完成该功能需要的坐标合集,其中大部分版本信息来自于父工程        

                3、我们的工程继承parent,引入starter后,通过依赖传递,就可以简单方便获取需要的jar包,并且不会存在版本冲突等问题。

SpringBoot配置

       1) 配置文件分类

                SpringBoot 是基础约定熟成的,所以很多配置都有默认值,但如果想使用自己的配置替换默认配置的话,就可以使用application.properties或者application.yml(application.yaml)进行配置。

  • properties:
server.port=8080
  • yml:(: 后要留空格)
  1. server:
  2. port: 8080
  3. #对象(map):键值对的集合
  4. person:
  5. name: zhangsan
  6. person: {name: zhangsan}#行内写法
  7. #数组:一组按次序排列的值
  8. address:
  9. - bejing
  10. - shanghai
  11. address: [beijing,shanghai]#行内写法
  12. #纯量:单个的、不可再分的值
  13. msg1: 'hello \n world' #单引号忽略转义字符
  14. msg2: "hello \n world" #双引号识别转义字符
  15. #参数引用
  16. name:zhgangsan
  17. person:
  18. name: ${name} #引用上边定义的name值

        *总结:在同一级目录下优先级为:properties>yml>yaml

        2)读取配置文件

                1)@Value

  1. @Value("${name}")//name要和配置文件的名字一致
  2. private String name;
  3. @Value("${person.name}")
  4. private String name2;
  5. @Value("${person.age}")
  6. private int age;
  7. @Value("${address[0]}")
  8. private int address1;
  9. @Value("${address[1]}")
  10. private int address1;

                2)Environment

  1. @Autowired
  2. private Emvironment env
  3. //获取
  4. System.out.println(env.getProperty('preson.name'));
  5. System.out.println(env.getProperty('address[0]'));

                3)@ConfigurationProperties

  1. @ConfigurationProperties(prefix = "person")//前缀指定那个变量
  2. public class Person {
  3. private String name;
  4. private int age;
  5. public String getName() {
  6. return name;
  7. }
  8. public void setName(String name) {
  9. this.name = name;
  10. }
  11. public int getAge() {
  12. return age;
  13. }
  14. public void setAge(int age) {
  15. this.age = age;
  16. }
  1. <dependency>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-configuration-processor</artifactId>
  4. <optional>true</optional>
  5. </dependency>

        3)多环境配置

                profile 可以让 Spring 对不同的环境提供不同配置的功能,可以通过激活、指定参数等方式快速切换环境。 换句话说,就是我们需要在不同的场景下使用不同的配置,profile的出现就是要解决我们多环境下切换配置复杂的问题。

                在实际开发环境中,我们存在开发环境的配置,部署环境的配置,测试环境的配置等等,里面的配置信息很多时,例如:端口、上下文路径、数据库配置等等,若每次切换环境时,我们都需要进行修改这些配置信息时,会比较麻烦,profile的出现就是为了解决这个问题。

                语法规则:application-{profile}.properties(.yaml/.yml)

                如果需要创建自定义的的properties文件时,可以用application-xxx.properties/yml的命名方式,这也是官方提供的,其中xxx可以根据自己的需求来定义。

                测试案例:创建application-dev.yml和application-prod.yml两个配置文件,指定不同的端口

        application-dev.yml
  1. # 开发环境
  2. jdbc:
  3. driverclass: com.mysql.jdbc.Driver
  4. url: jdbc:mysql://localhost:3306/java79?useUnicode=true&characterEncoding=utf8&serverTimezone=UTC
  5. username: zhangsan
  6. password: 123456
        application-test.yml
  1. # 测试环境
  2. jdbc:
  3. driverclass: com.mysql.jdbc.Driver
  4. url: jdbc:mysql://localhost:3306/java79?useUnicode=true&characterEncoding=utf8&serverTimezone=UTC
  5. username: test
  6. password: test

如果需要在两种环境下进行切换,只需要在application.yml中加入如下内容即可

  1. spring:
  2. profiles:
  3. active: dev
        常用环境

application.properties:主配置文件 application-dev.properties:开发环境配置文件 application-test.properties:测试环境配置文件 application-prod.properties:生产环境配置文件

SpringBoot热部署:

        

        添加依赖:

  1. <dependency>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-devtools</artifactId>
  4. <optional>true</optional>
  5. <scope>runtime</scope>
  6. </dependency>

        添加热部署配置:

  1. spring:
  2. #热部署配置
  3. devtools:
  4. restart:
  5. enabled: true #设置开启热部署
  6. additional-paths: src/main/java #重启目录
  7. exclude: WEB-INF/**
  8. freemarker:
  9. cache: false #页面不加载缓存修改及时生效

idea 添加配置 File -> settings ->Compiler 将Bulid project automatically 勾选上。

在这里插入图片描述

Ctrl + Shift + Alt + / 快捷键 弹出Maintenance窗口,选择Registry选项

在这里插入图片描述

找到key为compiler.automake.allow.when.app.running 将其勾选

在这里插入图片描述

重启项目即可

image-20220913161209773

整合Swagger3:

        1)导入依赖

  1. <!--文档框架 springboot2 + Openapi3-->
  2. <dependency>
  3. <groupId>com.github.xiaoymin</groupId>
  4. <artifactId>knife4j-openapi3-spring-boot-starter</artifactId>
  5. <version>4.3.0</version>
  6. </dependency>

        2)配置swagger

  1. # 文档框架配置
  2. springdoc:
  3. swagger-ui:
  4. path: /swagger-ui.html
  5. tags-sorter: alpha
  6. operations-sorter: alpha
  7. show-extensions: true
  8. api-docs:
  9. path: /v3/api-docs
  10. group-configs:
  11. - group: 'default'
  12. paths-to-match: '/**'
  13. packages-to-scan: com.xxgc.demo.controller #包扫描路径
  14. default-flat-param-object: false
  15. knife4j:
  16. enable: true
  17. setting:
  18. language: zh_cn
  19. swagger-model-name: 实体类列表
  20. basic:
  21. enable: true #进入文档需要输入密码
  22. username: admin
  23. password: 123456

        3)创建SwaggerConfig.java

  1. package com.xxgc.demo.config;
  2. import io.swagger.v3.oas.models.Components;
  3. import io.swagger.v3.oas.models.OpenAPI;
  4. import io.swagger.v3.oas.models.info.Info;
  5. import io.swagger.v3.oas.models.info.License;
  6. import io.swagger.v3.oas.models.media.StringSchema;
  7. import io.swagger.v3.oas.models.parameters.HeaderParameter;
  8. import io.swagger.v3.oas.models.security.SecurityRequirement;
  9. import io.swagger.v3.oas.models.security.SecurityScheme;
  10. import org.springdoc.core.GroupedOpenApi;
  11. import org.springframework.context.annotation.Bean;
  12. import org.springframework.context.annotation.Configuration;
  13. import org.springframework.http.HttpHeaders;
  14. /**
  15. * @Author:SJY
  16. * @Date :2023/9/5 - 09 - 05 - 16:35
  17. * thanksjava@qq.com
  18. */
  19. @Configuration
  20. public class SwaggerConfig {
  21. /**
  22. * 根据@Tag 上的排序,写入x-order
  23. *
  24. * @return the global open api customizer
  25. */
  26. /* @Bean
  27. public GlobalOpenApiCustomizer orderGlobalOpenApiCustomizer() {
  28. return openApi -> {
  29. if (openApi.getTags()!=null){
  30. openApi.getTags().forEach(tag -> {
  31. Map<String,Object> map=new HashMap<>();
  32. map.put("x-order", RandomUtil.randomInt(0,100));
  33. tag.setExtensions(map);
  34. });
  35. }
  36. if(openApi.getPaths()!=null){
  37. openApi.addExtension("x-test123","333");
  38. openApi.getPaths().addExtension("x-abb",RandomUtil.randomInt(1,100));
  39. }
  40. };
  41. }*/
  42. @Bean
  43. public GroupedOpenApi userApi(){
  44. String[] paths = { "/**" };
  45. String[] packagedToMatch = { "com.xiaominfo.knife4j.demo.web" };
  46. return GroupedOpenApi.builder().group("用户模块")
  47. .pathsToMatch(paths)
  48. .addOperationCustomizer((operation, handlerMethod) -> {
  49. return operation.addParametersItem(new HeaderParameter().name("groupCode").example("测试").description("集团code").schema(new StringSchema()._default("BR").name("groupCode").description("集团code")));
  50. })
  51. .packagesToScan(packagedToMatch).build();
  52. }
  53. @Bean
  54. public OpenAPI customOpenAPI() {
  55. return new OpenAPI()
  56. .info(new Info()
  57. .title("XXX用户系统API")
  58. .version("1.0")
  59. .description( "Knife4j集成springdoc-openapi示例")
  60. .termsOfService("http://doc.xiaominfo.com")
  61. .license(new License().name("Apache 2.0")
  62. .url("http://doc.xiaominfo.com"))
  63. ).addSecurityItem(new SecurityRequirement().addList(HttpHeaders.AUTHORIZATION))
  64. .components(new Components().addSecuritySchemes(HttpHeaders.AUTHORIZATION,new SecurityScheme()
  65. .name(HttpHeaders.AUTHORIZATION).type(SecurityScheme.Type.HTTP).scheme("bearer")));
  66. }
  67. }

        4)常用注解:

@Api(tags = "类上的描述")
@ApiOperation(value = "方法的描述", notes = "接口的描述")
@ApiModel(value = "模型的描述", description = "")
@ApiModelProperty("字段的描述")
  1. @Operation(summary = "获取信息测试",description = "测试用")
  2. @Parameters({
  3. @Parameter(name = "uname",description = "用户名",required = true,example = "admin"),
  4. @Parameter(name = "pword",description = "密码",required = true,example = "123"),
  5. })
  6. @Schema(description = "系统内部状态码")

       

 基于Lombok的多种用法:

        1)导入依赖

  1. <!--lombok 自动生成set、get、有参、无参、toString等常用方法-->
  2. <dependency>
  3. <groupId>org.projectlombok</groupId>
  4. <artifactId>lombok</artifactId>
  5. <version>1.18.28</version>
  6. <scope>provided</scope>
  7. </dependency>

        2)下载插件

                LomBok

        3)添加注解

  1. /**
  2. * 项目统一返回结果对象
  3. * lombok 自动生成set get方法
  4. * @Data set+get+tostring 方法
  5. * @AllArgsConstructor 全参构造方法
  6. * @NoArgsConstructor 无参构造方法
  7. */
  8. @Data
  9. @AllArgsConstructor
  10. @NoArgsConstructor
  11. public class Result<T> {
  12. @Schema(description = "系统内部状态码")
  13. private int code;
  14. @Schema(description = "系统信息")
  15. private String msg;
  16. @Schema(description = "返回数据")
  17. private T data;

基于HuTool进行数据脱敏

        1)导入依赖

  1. <dependency>
  2. <groupId>cn.hutool</groupId>
  3. <artifactId>hutool-all</artifactId>
  4. <version>5.8.16</version>
  5. </dependency>

        2)参考文档

HutoolHutool 官方文档icon-default.png?t=N7T8https://doc.hutool.cn/

  统一参数返回

        1.封装自定义参数返回类:

  1. package com.xgd.demo.controller.result;
  2. import com.fasterxml.jackson.annotation.JsonInclude;
  3. import io.swagger.v3.oas.annotations.media.Schema;
  4. import lombok.AllArgsConstructor;
  5. import lombok.Data;
  6. import lombok.NoArgsConstructor;
  7. /**
  8. * 项目统一返回结果对象
  9. * lombok 自动生成set get方法
  10. * @Data set+get+tostring 方法
  11. * @AllArgsConstructor 全参构造方法
  12. * @NoArgsConstructor 无参构造方法
  13. */
  14. @Data
  15. @AllArgsConstructor
  16. @NoArgsConstructor
  17. //如果值为空,在转json时就移除
  18. @JsonInclude(JsonInclude.Include.NON_NULL)
  19. public class Result<T> {
  20. @Schema(description = "系统内部状态码")
  21. private int code;
  22. @Schema(description = "系统信息")
  23. private String msg;
  24. @Schema(description = "返回数据")
  25. private T data;
  26. //--------------改变部分信息-----------------
  27. public Result msg(String msg){
  28. this.msg = msg;
  29. return this;
  30. }
  31. public Result code(int code){
  32. this.code = code;
  33. return this;
  34. }
  35. //请求成功的返回
  36. public static <T> Result<T> ok(T data){
  37. return new Result(200,"请求成功",data);
  38. }
  39. //请求成功的返回 不带参数
  40. public static <T> Result<T> ok(){
  41. return new Result(200,"请求成功",null);
  42. }
  43. //请求失败的返回 不带参数
  44. public static <T> Result<T> error(){
  45. return new Result(500,"请求失败",null);
  46. }
  47. //请求失败的返回 异常拦截使用
  48. public static <T> Result<T> error(int code,T data){
  49. return new Result(code,"请求失败",data);
  50. }
  51. }

全局异常处理

        1)创建全局异常处理类:GlobalExceptionHandler

        2)编写异常处理类型

  1. package com.xxgc.demo.controller.error;
  2. import com.xxgc.demo.controller.result.Result;
  3. import org.springframework.http.HttpStatus;
  4. import org.springframework.http.ResponseEntity;
  5. import org.springframework.http.converter.HttpMessageNotReadableException;
  6. import org.springframework.validation.BindException;
  7. import org.springframework.validation.ObjectError;
  8. import org.springframework.web.bind.MethodArgumentNotValidException;
  9. import org.springframework.web.bind.annotation.ControllerAdvice;
  10. import org.springframework.web.bind.annotation.ExceptionHandler;
  11. import org.springframework.web.bind.annotation.ResponseBody;
  12. import javax.validation.ConstraintViolation;
  13. import javax.validation.ConstraintViolationException;
  14. import javax.validation.ValidationException;
  15. import java.util.List;
  16. import java.util.stream.Collectors;
  17. /**
  18. * @Author: SJY
  19. * @Date :2023/9/8 - 09 - 08 - 14:37
  20. * 全局异常拦截器 @ControllerAdvice
  21. */
  22. @ControllerAdvice
  23. @ResponseBody
  24. public class GlobalExceptionHandler {
  25. //参数校验的拦截
  26. @ExceptionHandler(value = {BindException.class, ValidationException.class, MethodArgumentNotValidException.class})
  27. public ResponseEntity<Result> handleValidatedException(Exception e) {
  28. Result<List<String>> result = null;
  29. if (e instanceof MethodArgumentNotValidException) {
  30. MethodArgumentNotValidException ex = (MethodArgumentNotValidException) e;
  31. List<String> errorMessages = ex.getBindingResult().getAllErrors().stream()
  32. .map(ObjectError::getDefaultMessage)
  33. .collect(Collectors.toList());
  34. result = Result.error(HttpStatus.BAD_REQUEST.value(), errorMessages);
  35. } else if (e instanceof ConstraintViolationException) {
  36. ConstraintViolationException ex = (ConstraintViolationException) e;
  37. List<String> errorMessages = ex.getConstraintViolations().stream()
  38. .map(ConstraintViolation::getMessage)
  39. .collect(Collectors.toList());
  40. result = Result.error(HttpStatus.BAD_REQUEST.value(), errorMessages);
  41. } else if (e instanceof BindException) {
  42. BindException ex = (BindException) e;
  43. List<String> errorMessages = ex.getAllErrors().stream()
  44. .map(ObjectError::getDefaultMessage)
  45. .collect(Collectors.toList());
  46. result = Result.error(HttpStatus.BAD_REQUEST.value(), errorMessages);
  47. }
  48. return new ResponseEntity<>(result, HttpStatus.BAD_REQUEST);
  49. }
  50. //参数转换
  51. @ExceptionHandler(value = {HttpMessageNotReadableException.class})
  52. public ResponseEntity<Result> httpMessageNotReadableException(Exception e) {
  53. return new ResponseEntity<>(Result.error().msg("参数类型转换失败").errorMsg(e.getMessage()), HttpStatus.INTERNAL_SERVER_ERROR);
  54. }
  55. /**------------------------ 一定写在最下面 ----------------------------------**/
  56. /**------------------@TODO 开发环境和测试环境不用 只在生产环境用 留坑 (虚竹)-------------------**/
  57. //运行时的异常
  58. @ExceptionHandler(value = {Exception.class})
  59. public ResponseEntity<Result> runException(Exception e) {
  60. return new ResponseEntity<>(Result.error().msg("当前服务器压力大,请稍后重试"), HttpStatus.INTERNAL_SERVER_ERROR);
  61. }
  62. }

常用参数校验注解

  1. @Valid: 该注解用于指示一个参数或请求体需要进行验证。可以将该注解添加到参数上,表示该参数需要进行验证。
  2. @NotNull: 该注解用于指示一个参数不能为null。如果参数值为null,则会抛出一个异常。
  3. @NotEmpty: 该注解用于指示一个参数不能为空。如果参数值为空,则会抛出一个异常。
  4. @Size(min=, max=): 该注解用于指示一个参数的长度必须在指定范围内。其中,min表示参数长度的最小值,max表示参数长度的最大值。
  5. @Pattern(regex=, flags=): 该注解用于指示一个参数的值必须匹配指定的正则表达式。其中,regex表示正则表达式,flags表示正则表达式的标志。
  6. @Min(value=): 该注解用于指示一个参数的值必须大于等于指定的最小值。
  7. @Max(value=): 该注解用于指示一个参数的值必须小于等于指定的最大值。
  8. @DecimalMin(value=): 该注解用于指示一个参数的值必须大于等于指定的最小十进制值。
  9. @DecimalMax(value=): 该注解用于指示一个参数的值必须小于等于指定的最大十进制值。
  10. @Email(message=): 该注解用于指示一个参数的值必须是一个有效的电子邮件地址。如果参数值不是有效的电子邮件地址,则会抛出一个异常,并且可以在message中指定异常消息。
  11. @EqualTo(referringParamName=): 该注解用于指示一个参数的值必须等于另一个参数的值。其中,referringParamName表示另一个参数的名称。

自定义注解

        1)拦截器方式:

        2)切面AOP方式:

                导入依赖:
  1. <!--SpringAOP的包-->
  2. <dependency>
  3. <groupId>org.springframework.boot</groupId>
  4. <artifactId>spring-boot-starter-aop</artifactId>
  5. </dependency>
                 
        创建一个自定义注解:
  1. import java.lang.annotation.ElementType;
  2. import java.lang.annotation.Retention;
  3. import java.lang.annotation.RetentionPolicy;
  4. import java.lang.annotation.Target;
  5. @Target(ElementType.METHOD)
  6. @Retention(RetentionPolicy.RUNTIME)
  7. public @interface CheckIdCard {
  8. }
       创建一个Aspect来处理这个注解: 
  1. import org.aspectj.lang.ProceedingJoinPoint;
  2. import org.aspectj.lang.annotation.Around;
  3. import org.aspectj.lang.annotation.Aspect;
  4. import org.springframework.stereotype.Component;
  5. import java.text.ParseException;
  6. import java.text.SimpleDateFormat;
  7. import java.util.Date;
  8. @Aspect
  9. @Component
  10. public class IdCardInterceptor {
  11. private SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
  12. @Around("execution(* com.yourpackage..*(..)) && args(idCard)")'execution(* com.yourpackage..*(..))' 是一个方法匹配器,表示这个环绕建议应用于com.yourpackage包及其子包中所有的方法。'args(idCard)' 是一个参数匹配器,表示这个环绕建议只应用于含有idCard参数的方法
  13. //@Around("@annotation(CheckIdCard)")表示这个方法会在带有CheckIdCard注解的方法被调用之前执行
  14. public Object checkIdCard(ProceedingJoinPoint joinPoint, String idCard) throws ParseException {
  15. try {
  16. Date birthDate = sdf.parse(idCard.substring(6, 14)); // 假设身份证号的出生日期在从左起第6到14位
  17. Date currentDate = new Date();
  18. if (currentDate.after(birthDate)) {
  19. throw new RuntimeException("身份证号出生日期不能在当前日期之前");
  20. }
  21. } catch (ParseException e) {
  22. throw new RuntimeException("身份证号格式不正确", e);
  23. }
  24. return joinPoint.proceed();
  25. }
  26. }

         3)巧用ConstraintValidator接口方式:

                        创建自定义注解类:
  1. import java.lang.annotation.Documented;
  2. import java.lang.annotation.ElementType;
  3. import java.lang.annotation.Retention;
  4. import java.lang.annotation.RetentionPolicy;
  5. import java.lang.annotation.Target;
  6. import javax.validation.Constraint;
  7. import javax.validation.Payload;
  8. @Documented
  9. @Constraint(validatedBy = {IdCardBirthdayBeforeCurrentDateValidator.class})//用于指定一个或多个验证器来验证类或字段的约束。在这个例子中,它指定了 IdNoValidator 类作为验证器来验证该类或字段的约束。具体来说,它可以用于验证身份证号码、账号、手机号码等各种类型的标识号码。具体的验证逻辑需要在 IdNoValidator 类中实现。
  10. @Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})
  11. @Retention(RetentionPolicy.RUNTIME)
  12. public @interface IdCardBirthdayBeforeCurrentDate {
  13. String message() default "身份证号码的出生日期不能在当前日期之前";
  14. Class<?>[] groups() default {};
  15. Class<? extends Payload>[] payload() default {};
  16. String regexp() default "^[1-9]\\d{5}(18|19|20)\\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\\d{3}[0-9Xx]$";
  17. }

        在这个注解类中,我们定义了一个regexp属性,用于存储身份证号码的正则表达式。这个正则表达式可以匹配所有的有效身份证号码,包括出生日期在当前日期之前的身份证号码。


                创建一个自定义验证器类,该类需要实现ConstraintValidator接口。在这个类中,您可以实现isValid方法,用于检查身份证号码的出生日期是否在当前日期之前。
  1. import javax.validation.ConstraintValidator;
  2. import javax.validation.ConstraintValidatorContext;
  3. import java.time.LocalDate;
  4. import java.time.format.DateTimeFormatter;
  5. import java.util.regex.Matcher;
  6. import java.util.regex.Pattern;
  7. public class IdCardBirthdayBeforeCurrentDateValidator implements ConstraintValidator<IdCardBirthdayBeforeCurrentDate, String> {
  8. private String regexp;
  9. @Override
  10. public void initialize(IdCardBirthdayBeforeCurrentDate constraintAnnotation) {
  11. this.regexp = constraintAnnotation.regexp();
  12. }
  13. @Override
  14. public boolean isValid(String value, ConstraintValidatorContext context) {
  15. if (value == null || value.isEmpty()) {
  16. return true;
  17. }
  18. Pattern pattern = Pattern.compile(regexp);
  19. Matcher matcher = pattern.matcher(value);
  20. if (!matcher.matches()) {
  21. return false;
  22. }
  23. //int year = Integer.parseInt(idNo.substring(6, 10));
  24. //int month = Integer.parseInt(idNo.substring(10, 12));
  25. //int day = Integer.parseInt(idNo.substring(12, 14));
  26. // Calendar calendar = Calendar.getInstance();
  27. //calendar.set(year, month, day);
  28. //return calendar.getTime().before(new Date());
  29. String birthdayStr = value.substring(6, 14);
  30. DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd");
  31. LocalDate birthday = LocalDate.parse(birthdayStr, formatter);
  32. LocalDate currentDate = LocalDate.now();
  33. return birthday.isBefore(currentDate);
  34. }
  35. }

        在这个验证器类中,我们实现了isValid方法,用于检查身份证号码的出生日期是否在当前日期之前。我们首先检查身份证号码是否符合正则表达式。如果不符合,则返回false。如果符合,则将出生日期转换为LocalDate类型,并与当前日期进行比较。如果出生日期在当前日期之前,则返回true,否则返回false。


                使用@IdCardBirthdayBeforeCurrentDate注解来指定身份证号码的正则表达式
  1. import javax.validation.constraints.Pattern;
  2. public class User {
  3. @Pattern(regexp = "^[1-9]\\d{5}(18|19|20)\\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\\d{3}[0-9Xx]$", message = "身份证号码的出生日期不能在当前日期之前")
  4. private String idCard;
  5. // getters and setters
  6. }
                ConstraintValidator介绍:
                                1)ConstraintValidator是Java中的一种工具类,用于做实体类字段校验
                                2)ConstraintValidator接口是Hibernate Validator库中的一个重要组件,用于实现自定义的验证规则。这些规则可以应用于Java Bean中的字段,以进行各种复杂的数据验证。
           3)ConstraintValidator接口有两个主要方法:initializeisValid
  1. initialize方法:此方法在验证器创建时被调用,可以用于执行任何必要的初始化工作,例如,加载配置信息、初始化状态等。
  2. isValid方法:此方法用于执行实际的验证逻辑。它接收两个参数:一个是待验证的实体对象(即要进行验证的Java Bean),另一个是ConstraintValidatorContext对象,它可以提供额外的上下文信息,如字段名称、字段索引等。通过这个方法,你可以实现自定义的验证逻辑,例如检查数据是否满足特定的格式、范围、业务规则等

整合JWT

        1)导入依赖

  1. <dependency>
  2. <groupId>com.auth0</groupId>
  3. <artifactId>java-jwt</artifactId>
  4. <version>3.4.0</version>
  5. </dependency>

        2)如何使用

        JWT工具-JWTUtil | HutoolHutool 官方文档icon-default.png?t=N7T8https://doc.hutool.cn/pages/JWTUtil/#%E4%BD%BF%E7%94%A8

整合mybatis-plus

        1)添加mybatis-plus依赖

  1. <dependency>
  2. <groupId>com.baomidou</groupId>
  3. <artifactId>mybatis-plus-boot-starter</artifactId>
  4. <version>最新版本</version>
  5. </dependency>

        2)导入jdbc的mysql驱动依赖

  1. <dependency>
  2. <groupId>mysql</groupId>
  3. <artifactId>mysql-connector-java</artifactId>
  4. <version>8.0.26</version>
  5. </dependency>

        3)配置application.yml

  1. spring:
  2. # 配置数据源信息
  3. datasource:
  4. # 配置数据源类型
  5. type: com.zaxxer.hikari.HikariDataSource
  6. # 配置连接数据库信息
  7. driver-class-name: com.mysql.cj.jdbc.Driver
  8. url: jdbc:mysql://localhost:3306/mybatis_plus?characterEncoding=utf-8&useSSL=false
  9. username: root
  10. password: 123456
  11. # MyBatis Plus配置
  12. mybatis-plus:
  13. # 搜索指定包别名
  14. typeAliasesPackage: com.ruoyi.**.domain
  15. # 配置mapper的扫描,找到所有的mapper.xml映射文件
  16. mapperLocations: classpath*:mapper/**/*Mapper.xml
  17. configLocation:
  18. classpath:mybatis/mybatis-config.xml # 加载全局的配置文件
  19. log-impl: org.apache.ibatis.logging.stdout.StdOutImpl #是否打印sql语句

整合Redis

        1)导入依赖

  1. <!-- redis 缓存操作 -->
  2. <dependency>
  3. <groupId>org.springframework.boot</groupId>
  4. <artifactId>spring-boot-starter-data-redis</artifactId>
  5. </dependency>

        2) 配置application.yml

  1. # redis 配置
  2. redis:
  3. # 地址
  4. host: localhost
  5. # 端口,默认为6379
  6. port: 6379
  7. # 数据库索引
  8. database: 0
  9. # 密码
  10. password:
  11. # 连接超时时间
  12. timeout: 10s
  13. lettuce:
  14. pool:
  15. # 连接池中的最小空闲连接
  16. min-idle: 0
  17. # 连接池中的最大空闲连接
  18. max-idle: 8
  19. # 连接池的最大数据库连接数
  20. max-active: 8
  21. # #连接池最大阻塞等待时间(使用负值表示没有限制)
  22. max-wait: -1ms

        3)新建redis.config配置类,配置模板

  1. @Configuration
  2. public class RedisConfig {
  3. @Bean
  4. public RedisSerializer<Object> redisSerializer() {
  5. //创建JSON序列化器
  6. Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer<>(Object.class);
  7. ObjectMapper objectMapper = new ObjectMapper();
  8. objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
  9. objectMapper.configure(MapperFeature.USE_ANNOTATIONS, false);
  10. objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
  11. objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
  12. //解决localDateTime的序列化问题
  13. objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
  14. objectMapper.registerModule(new JavaTimeModule());
  15. objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
  16. //必须设置,否则无法将JSON转化为对象,会转化成Map类型
  17. objectMapper.disableDefaultTyping();
  18. objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
  19. //objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance,ObjectMapper.DefaultTyping.NON_FINAL);
  20. serializer.setObjectMapper(objectMapper);
  21. return serializer;
  22. }
  23. @Bean
  24. public RedisCacheManager redisCacheManager(RedisConnectionFactory redisConnectionFactory) {
  25. RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory);
  26. //设置Redis缓存有效期为1天
  27. RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
  28. .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer())).entryTtl(Duration.ofDays(1));
  29. return new RedisCacheManager(redisCacheWriter, redisCacheConfiguration);
  30. }
  31. @Bean
  32. @ConditionalOnMissingBean
  33. public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
  34. RedisSerializer<Object> serializer = redisSerializer();
  35. RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
  36. redisTemplate.setConnectionFactory(redisConnectionFactory);
  37. redisTemplate.setKeySerializer(new StringRedisSerializer());
  38. redisTemplate.setValueSerializer(serializer);
  39. redisTemplate.setHashKeySerializer(new StringRedisSerializer());
  40. redisTemplate.setHashValueSerializer(serializer);
  41. redisTemplate.afterPropertiesSet();
  42. return redisTemplate;
  43. }
  44. }

        3)新建RedisUtil.java,创建工具类

  1. @Component
  2. public class RedisUtil {
  3. @Resource
  4. private RedisTemplate<String, Object> redisTemplate;
  5. // =============================Common 基础============================
  6. /**
  7. * 指定缓存失效时间
  8. *
  9. * @param key 键
  10. * @param time 时间(秒)
  11. */
  12. public boolean expire(String key, long time) {
  13. try {
  14. if (time > 0) {
  15. redisTemplate.expire(key, time, TimeUnit.SECONDS);
  16. }
  17. return true;
  18. } catch (Exception e) {
  19. e.printStackTrace();
  20. return false;
  21. }
  22. }
  23. /**
  24. * 根据key 获取过期时间
  25. *
  26. * @param key 键 不能为null
  27. * @return 时间(秒) 返回0代表为永久有效
  28. */
  29. public long getExpire(String key) {
  30. // 如果返回值为 null,则返回 0L
  31. return redisTemplate.getExpire(key, TimeUnit.SECONDS);
  32. }
  33. /**
  34. * 判断key是否存在
  35. *
  36. * @param key 键
  37. * @return true 存在 false不存在
  38. */
  39. public boolean hasKey(String key) {
  40. try {
  41. return Boolean.TRUE.equals(redisTemplate.hasKey(key));
  42. } catch (Exception e) {
  43. e.printStackTrace();
  44. return false;
  45. }
  46. }
  47. /**
  48. * 删除缓存
  49. *
  50. * @param key 可以传一个值 或多个
  51. */
  52. @SuppressWarnings("unchecked")
  53. public void del(String... key) {
  54. if (key != null && key.length > 0) {
  55. if (key.length == 1) {
  56. redisTemplate.delete(key[0]);
  57. } else {
  58. redisTemplate.delete((Collection<String>) CollectionUtils.arrayToList(key));
  59. }
  60. }
  61. }
  62. // ============================String 字符串=============================
  63. /**
  64. * 普通缓存获取
  65. *
  66. * @param key 键
  67. * @return
  68. */
  69. public Object get(String key) {
  70. return key == null ? null : redisTemplate.opsForValue().get(key);
  71. }
  72. /**
  73. * 普通缓存放入
  74. *
  75. * @param key 键
  76. * @param value 值
  77. * @return true成功 false失败
  78. */
  79. public boolean set(String key, Object value) {
  80. try {
  81. redisTemplate.opsForValue().set(key, value);
  82. return true;
  83. } catch (Exception e) {
  84. e.printStackTrace();
  85. return false;
  86. }
  87. }
  88. /**
  89. * 普通缓存放入并设置时间
  90. *
  91. * @param key 键
  92. * @param value 值
  93. * @param time 时间(秒) time要大于0 如果time小于等于0 将设置无限期
  94. * @return true成功 false 失败
  95. */
  96. public boolean set(String key, Object value, long time) {
  97. try {
  98. if (time > 0) {
  99. redisTemplate.opsForValue().set(key, value, time,
  100. TimeUnit.SECONDS);
  101. } else {
  102. set(key, value);
  103. }
  104. return true;
  105. } catch (Exception e) {
  106. e.printStackTrace();
  107. return false;
  108. }
  109. }
  110. /**
  111. * 递增
  112. *
  113. * @param key 键
  114. * @param delta 要增加几(大于0)
  115. */
  116. public long incr(String key, long delta) {
  117. if (delta < 0) {
  118. throw new RuntimeException("递增因子必须大于0");
  119. }
  120. return redisTemplate.opsForValue().increment(key, delta);
  121. }
  122. /**
  123. * 递减
  124. *
  125. * @param key 键
  126. * @param delta 要减少几(小于0)
  127. */
  128. public long decr(String key, long delta) {
  129. if (delta < 0) {
  130. throw new RuntimeException("递减因子必须大于0");
  131. }
  132. return redisTemplate.opsForValue().increment(key, -delta);
  133. }
  134. // ===============================List 列表=================================
  135. /**
  136. * 获取list缓存的内容
  137. *
  138. * @param key 键
  139. * @param start 开始
  140. * @param end 结束 0 到 -1代表所有值
  141. */
  142. public List<Object> lGet(String key, long start, long end) {
  143. try {
  144. return redisTemplate.opsForList().range(key, start, end);
  145. } catch (Exception e) {
  146. e.printStackTrace();
  147. return null;
  148. }
  149. }
  150. /**
  151. * 获取list缓存的长度
  152. *
  153. * @param key 键
  154. */
  155. public long lGetListSize(String key) {
  156. try {
  157. return redisTemplate.opsForList().size(key);
  158. } catch (Exception e) {
  159. e.printStackTrace();
  160. return 0;
  161. }
  162. }
  163. /**
  164. * 通过索引 获取list中的值
  165. *
  166. * @param key 键
  167. * @param index 索引 index>=0时, 0 表头,1 第二个元素,依次类推;index<0
  168. * 时,-1,表尾,-2倒数第二个元素,依次类推
  169. */
  170. public Object lGetIndex(String key, long index) {
  171. try {
  172. return redisTemplate.opsForList().index(key, index);
  173. } catch (Exception e) {
  174. e.printStackTrace();
  175. return null;
  176. }
  177. }
  178. /**
  179. * 将list放入缓存
  180. *
  181. * @param key 键
  182. * @param value 值
  183. */
  184. public boolean lSet(String key, Object value) {
  185. try {
  186. redisTemplate.opsForList().rightPush(key, value);
  187. return true;
  188. } catch (Exception e) {
  189. e.printStackTrace();
  190. return false;
  191. }
  192. }
  193. /**
  194. * 将list放入缓存
  195. *
  196. * @param key 键
  197. * @param value 值
  198. * @param time 时间(秒)
  199. */
  200. public boolean lSet(String key, Object value, long time) {
  201. try {
  202. redisTemplate.opsForList().rightPush(key, value);
  203. if (time > 0) {
  204. expire(key, time);
  205. }
  206. return true;
  207. } catch (Exception e) {
  208. e.printStackTrace();
  209. return false;
  210. }
  211. }
  212. /**
  213. * 将list放入缓存
  214. *
  215. * @param key 键
  216. * @param value 值
  217. * @return 赋值结果
  218. */
  219. public boolean lSet(String key, List<Object> value) {
  220. try {
  221. redisTemplate.opsForList().rightPushAll(key, value);
  222. return true;
  223. } catch (Exception e) {
  224. e.printStackTrace();
  225. return false;
  226. }
  227. }
  228. /**
  229. * 将list放入缓存
  230. *
  231. * @param key 键
  232. * @param value 值
  233. * @param time 时间(秒)
  234. * @return 赋值结果
  235. */
  236. public boolean lSet(String key, List<Object> value, long time) {
  237. try {
  238. redisTemplate.opsForList().rightPushAll(key, value);
  239. if (time > 0) {
  240. expire(key, time);
  241. }
  242. return true;
  243. } catch (Exception e) {
  244. e.printStackTrace();
  245. return false;
  246. }
  247. }
  248. /**
  249. * 根据索引修改list中的某条数据
  250. *
  251. * @param key 键
  252. * @param index 索引
  253. * @param value 值
  254. * @return 赋值结果
  255. */
  256. public boolean lUpdateIndex(String key, long index, Object value) {
  257. try {
  258. redisTemplate.opsForList().set(key, index, value);
  259. return true;
  260. } catch (Exception e) {
  261. e.printStackTrace();
  262. return false;
  263. }
  264. }
  265. /**
  266. * 移除N个值为value
  267. *
  268. * @param key 键
  269. * @param count 移除多少个
  270. * @param value 值
  271. * @return 移除的个数
  272. */
  273. public long lRemove(String key, long count, Object value) {
  274. try {
  275. return redisTemplate.opsForList().remove(key, count, value);
  276. } catch (Exception e) {
  277. e.printStackTrace();
  278. return 0;
  279. }
  280. }
  281. // ============================Set 集合=============================
  282. /**
  283. * 根据key获取Set中的所有值
  284. *
  285. * @param key 键
  286. */
  287. public Set<Object> sGet(String key) {
  288. try {
  289. return redisTemplate.opsForSet().members(key);
  290. } catch (Exception e) {
  291. e.printStackTrace();
  292. return null;
  293. }
  294. }
  295. /**
  296. * 根据value从一个set中查询,是否存在
  297. *
  298. * @param key 键
  299. * @param value 值
  300. * @return true 存在 false不存在
  301. */
  302. public boolean sHasKey(String key, Object value) {
  303. try {
  304. return redisTemplate.opsForSet().isMember(key, value);
  305. } catch (Exception e) {
  306. e.printStackTrace();
  307. return false;
  308. }
  309. }
  310. /**
  311. * 将数据放入set缓存
  312. *
  313. * @param key 键
  314. * @param values 值 可以是多个
  315. * @return 成功个数
  316. */
  317. public long sSet(String key, Object... values) {
  318. try {
  319. return redisTemplate.opsForSet().add(key, values);
  320. } catch (Exception e) {
  321. e.printStackTrace();
  322. return 0;
  323. }
  324. }
  325. /**
  326. * 将set数据放入缓存
  327. *
  328. * @param key 键
  329. * @param time 时间(秒)
  330. * @param values 值 可以是多个
  331. * @return 成功个数
  332. */
  333. public long sSetAndTime(String key, long time, Object... values) {
  334. try {
  335. Long count = redisTemplate.opsForSet().add(key, values);
  336. if (time > 0) {
  337. expire(key, time);
  338. }
  339. return count;
  340. } catch (Exception e) {
  341. e.printStackTrace();
  342. return 0;
  343. }
  344. }
  345. /**
  346. * 获取set缓存的长度
  347. *
  348. * @param key 键
  349. */
  350. public long sGetSetSize(String key) {
  351. try {
  352. return redisTemplate.opsForSet().size(key);
  353. } catch (Exception e) {
  354. e.printStackTrace();
  355. return 0;
  356. }
  357. }
  358. /**
  359. * 移除值为value的
  360. *
  361. * @param key 键
  362. * @param values 值 可以是多个
  363. * @return 移除的个数
  364. */
  365. public long setRemove(String key, Object... values) {
  366. try {
  367. return redisTemplate.opsForSet().remove(key, values);
  368. } catch (Exception e) {
  369. e.printStackTrace();
  370. return 0;
  371. }
  372. }
  373. // ================================Hash 哈希=================================
  374. /**
  375. * HashGet
  376. *
  377. * @param key 键 不能为null
  378. * @param item 项 不能为null
  379. */
  380. public Object hget(String key, String item) {
  381. return redisTemplate.opsForHash().get(key, item);
  382. }
  383. /**
  384. * 获取hashKey对应的所有键值
  385. *
  386. * @param key 键
  387. * @return 对应的多个键值
  388. */
  389. public Map<Object, Object> hmget(String key) {
  390. return redisTemplate.opsForHash().entries(key);
  391. }
  392. /**
  393. * HashSet
  394. *
  395. * @param key 键
  396. * @param map 对应多个键值
  397. */
  398. public boolean hmset(String key, Map<String, Object> map) {
  399. try {
  400. redisTemplate.opsForHash().putAll(key, map);
  401. return true;
  402. } catch (Exception e) {
  403. e.printStackTrace();
  404. return false;
  405. }
  406. }
  407. /**
  408. * HashSet 并设置时间
  409. *
  410. * @param key 键
  411. * @param map 对应多个键值
  412. * @param time 时间(秒)
  413. * @return true成功 false失败
  414. */
  415. public boolean hmset(String key, Map<String, Object> map, long time) {
  416. try {
  417. redisTemplate.opsForHash().putAll(key, map);
  418. if (time > 0) {
  419. expire(key, time);
  420. }
  421. return true;
  422. } catch (Exception e) {
  423. e.printStackTrace();
  424. return false;
  425. }
  426. }
  427. /**
  428. * 向一张hash表中放入数据,如果不存在将创建
  429. *
  430. * @param key 键
  431. * @param item 项
  432. * @param value 值
  433. * @return true 成功 false失败
  434. */
  435. public boolean hset(String key, String item, Object value) {
  436. try {
  437. redisTemplate.opsForHash().put(key, item, value);
  438. return true;
  439. } catch (Exception e) {
  440. e.printStackTrace();
  441. return false;
  442. }
  443. }
  444. /**
  445. * 向一张hash表中放入数据,如果不存在将创建
  446. *
  447. * @param key 键
  448. * @param item 项
  449. * @param value 值
  450. * @param time 时间(秒) 注意:如果已存在的hash表有时间,这里将会替换原有的时间
  451. * @return true 成功 false失败
  452. */
  453. public boolean hset(String key, String item, Object value, long time) {
  454. try {
  455. redisTemplate.opsForHash().put(key, item, value);
  456. if (time > 0) {
  457. expire(key, time);
  458. }
  459. return true;
  460. } catch (Exception e) {
  461. e.printStackTrace();
  462. return false;
  463. }
  464. }
  465. /**
  466. * 删除hash表中的值
  467. *
  468. * @param key 键 不能为null
  469. * @param item 项 可以使多个 不能为null
  470. */
  471. public void hdel(String key, Object... item) {
  472. redisTemplate.opsForHash().delete(key, item);
  473. }
  474. /**
  475. * 判断hash表中是否有该项的值
  476. *
  477. * @param key 键 不能为null
  478. * @param item 项 不能为null
  479. * @return true 存在 false不存在
  480. */
  481. public boolean hHasKey(String key, String item) {
  482. return redisTemplate.opsForHash().hasKey(key, item);
  483. }
  484. /**
  485. * hash递增 如果不存在,就会创建一个 并把新增后的值返回
  486. *
  487. * @param key 键
  488. * @param item 项
  489. * @param by 要增加几(大于0)
  490. */
  491. public double hincr(String key, String item, double by) {
  492. return redisTemplate.opsForHash().increment(key, item, by);
  493. }
  494. /**
  495. * hash递减
  496. *
  497. * @param key 键
  498. * @param item 项
  499. * @param by 要减少记(小于0)
  500. */
  501. public double hdecr(String key, String item, double by) {
  502. return redisTemplate.opsForHash().increment(key, item, -by);
  503. }
  504. }

        4)使用redis

  1. //web层 打通 service层
  2. @Autowired
  3. private IDemoUsersService iDemoUsersService;
  4. @Autowired
  5. private RedisUtil redisUtil;//注入redis工具类
  6. @GetMapping("/getUserInfoById")
  7. public Result getUserInfoById(Long userId){
  8. String key = "user"+userId;
  9. //缓存是否有这个对象
  10. boolean b = redisUtil.hasKey(key);
  11. if(b){//如果有
  12. log.info("我是缓存中获取的");
  13. DemoUsers demoUsers = (DemoUsers) redisUtil.get(key);
  14. return Result.ok(demoUsers);
  15. }else{//如果缓存没有
  16. log.info("我是数据库获取的");
  17. DemoUsers demoUsers = iDemoUsersService.getById(userId);
  18. //往缓存中丢一份
  19. redisUtil.set(key,demoUsers);
  20. return Result.ok(demoUsers);
  21. }
  22. }

5)相关注解:

  1. @cacheable(cacheNames="userInfo",key="#userId",unless="#userId == 1",condition="#userId == 1")
  2. //缓存组件名、 key名、 userId=1 时不使用缓存、userId=2 时使用缓存

        

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/weixin_40725706/article/detail/574992
推荐阅读
相关标签
  

闽ICP备14008679号