SpringBoot参数校验

news/2024/2/29 17:21:27/文章来源:https://blog.csdn.net/m0_52191385/article/details/135546002

介绍

在开发现代应用程序时,数据验证是确保用户输入的正确性和应用程序数据完整性的关键方面。Spring Boot 提供了强大的数据验证机制,使开发者能够轻松地执行验证操作。本文将深入介绍 Spring Boot 中的 Validation,以及如何在应用程序中正确使用它。

为什么使用数据验证?

  • 1.用户输入的正确性:数据验证是确保用户输入的正确性的一种重要手段。通过验证用户输入的数据,可以防止无效或错误的数据进入应用程序,提高数据的质量。例如:系统中的备注字段数据库中对应的长度是256,如果用户输入的备注超过这个长度值,那么就会导致mysql报Data too long
  • 2. 数据完整性:  数据完整性是指数据在存储和传输过程中的准确性和一致性。数据验证有助于确保数据满足特定的格式、长度、范围等要求,从而提高数据的完整性。
  1. 安全性:  数据验证也是保障应用程序安全性的关键因素。通过验证用户输入,可以防范一些潜在的安全威胁,例如 SQL 注入、跨站脚本攻击等。
  2. 业务规则的执行:  在应用程序中,通常存在一些业务规则,例如某个字段不能为空、日期范围必须在某个特定范围内等。通过数据验证,可以确保这些业务规则在应用程序中得到正确执行。

手动数据校验的痛点

日常开发中,有些写项目可能没有采用Spring Validator,采用的是在代码中手动校验数据。但是手动校验数据会带来代码冗余、错误处理的一致性以及业务规则的维护的一些痛点。

  • 代码冗余的手动校验逻辑,导致代码中大量的if-else
public ResponseEntity<String> registerUser(UserRegistrationRequest request) {if (request == null) {return ResponseEntity.badRequest().body("Request cannot be null");}if (StringUtils.isBlank(request.getUsername())) {return ResponseEntity.badRequest().body("Username cannot be blank");}if (StringUtils.length(request.getPassword()) < 6) {return ResponseEntity.badRequest().body("Password must be at least 6 characters long");}// 处理用户注册逻辑return ResponseEntity.ok("User registered successfully");
}
  • 缺乏统一的错误处理机制
  • 业务规则维护的困难
    随着业务规则的增加,手动编写的校验逻辑可能变得庞大且难以维护。修改和扩展校验规则可能需要修改多个地方,增加了维护成本。
  • 缺乏验证组的支持
    手动校验通常不支持验证组的概念,难以根据不同场景执行不同的验证规则。
  • 不易于集成前端验证
    手动校验不易与前端验证框架集成,导致前后端验证逻辑可能不一致。

通过引入 Spring Validator,我们能够有效解决这些痛点,提高代码的可读性、可维护性,并确保校验逻辑的一致性。

Spring Boot 中的 Validation 概述

因Springboot的spring-boot-starter-web默认内置了Hibernate-Validator(Spring boot 2.3以前版本),虽然Hibernate-Validator也能做到数据校验,但是考虑到spring-boot-starter-validation 是一个抽象层,使得验证框架的具体实现变得可插拔。这意味着,除了 Hibernate Validator,开发者可以选择其他符合 Bean Validation 规范的实现。所以我们可以手动引入spring-boot-starter-validation实现数据验证。

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-validation</artifactId>
</dependency><dependency>  <groupId>org.springframework.boot</groupId>  <artifactId>spring-boot-starter-web</artifactId>  
</dependency>

spring-boot-starter-validation 不仅支持 JSR-303(Bean Validation 1.0)规范,还提供了对 JSR-380(Bean Validation 2.0)规范的全面支持。这使得开发者可以利用 Bean Validation 2.0 的新特性,更灵活地定义验证规则,包括对集合、嵌套对象的验证等。

通过在实体类的字段上使用标准的 Bean Validation 注解(如 @NotBlank@Size@Email 等),我们能够直观地定义数据的验证规则。这些验证规则会在应用程序的不同层次(如控制器层)生效,确保输入数据的正确性。

基本用法

Spring Boot Validation 提供了一系列注解,用于在实体类中定义验证规则。以下是一些常用的校验相关的注解及其功能以及用法:

  • @NotNull  校验元素值不能为 null。如果元素为null,则验证失败。通常用于字段级别的验证。
@NotNull(message = "Name cannot be null")
private String name;
  • @NotBlank  校验字符串元素值不能为 null 或空字符串。必须包含至少一个非空格字符(即执行trim()之后不为'')。如果元素为null或者‘‘,则验证失败。通常用于String类型的字段校验。
@NotBlank(message = "Username cannot be blank")
private String username;
  • NotEmpty  校验集合元素或数组元素或者字符串是否非空。通常作用于集合字段或数组字段,此时需要集合或者数字的元素个数大于0。也可以作用于字符串,此时校验字符串不能为null或空串(可以是一个空格)。注意与@NotBlank的使用区别。
@NotEmpty(message = "List cannot be empty")
private List<String> items;
  • @Length  校验字符串元素的长度。作用于字符串。注:Hibernate-Validator中注解,等同于spring-boot-starter-validation中的@Size
@Length(min = 5, max = 20, message = "Length must be between 5 and 20 characters")
private String username;
  • @Size  校验集合元素个数或字符串的长度在指定范围内。在集合或字符串字段上添加 @Size 注解。
@Size(min = 1, max = 10, message = "Number of items must be between 1 and 10")
private List<String> items;@Size(min = 5, max = 20, message = "Length must be between 5 and 20 characters")
private String username;
  • @Min  校验数字元素的最小值。
@Min(value = 18, message = "Age must be at least 18")
private int age;
  • @Max  校验数字元素的最大值。
@Max(value = 100, message = "Age must not exceed 100")
private int age;
  • @DecimalMax  作用于BigDecimal类型字段, 校验字段的最大值,支持比较的值为字符串表示的十进制数。通常搭配它的inclusive()使用,区别边界问题。value 属性表示最大值,inclusive 属性表示是否包含最大值。
@DecimalMax(value = "100.00", inclusive = true, message = "Value must be less than or equal to 100.00")
private BigDecimal amount;
  • @DecimalMin  作用于BigDecimal类型字段, 校验字段的最小值,支持比较的值为字符串表示的十进制数。通常搭配它的inclusive()使用,区别边界问题。value 属性表示最小值,inclusive 属性表示是否包含最小值。
@DecimalMin(value = "0.00", inclusive = false, message = "Value must be greater than 0.00")
private BigDecimal amount;
  • @Email  校验字符串元素是否为有效的电子邮件地址。可以通过regexp自定义邮箱匹配正则。
@Email(message = "Invalid email address")
private String email;
  • @Pattern  根据正则表达式校验字符串元素的格式。
@Pattern(regexp = "[a-zA-Z0-9]+", message = "Only alphanumeric characters are allowed")
private String username;
  • @Digits  校验数字元素的整数部分和小数部分的位数。作用于BigDecimalBigInteger,字符串,以及byteshort,intlong以及它们的包装类型。
@Digits(integer = 5, fraction = 2, message = "Number must have up to 5 integer digits and 2 fraction digits")
private BigDecimal amount;
  • @Past  校验日期或时间元素是否在当前时间之前。即是否是过去时间。作用于Date相关类型的字段。
@Past(message = "Date must be in the past")
private LocalDate startDate;
  • @Future  校验日期或时间元素是否在当前时间之后。即是否是未来时间。作用于Date相关类型的字段。
@Future(message = "Date must be in the future")
private LocalDate endDate;

注:以上只罗列部分注解以及它们的功能,其余他们的字段属性并没有详细说明,其他注解以及详细的说明需要去看源码。

用法示例
1.定义接口入参请求参数
@Data  
public class UserCreateRequestVO {  @NotBlank(message = "请输入用户名")  @Size(max = 128, message = "用户名长度最大为128个字符")  private String userName;  @Email(message = "请填写正确的邮箱地址")  private String email;  @Min(value = 18, message = "用户年龄必须大于18岁")  @Max(value = 60, message = "用户年龄必须小于60岁")  private Integer age;  @NotEmpty(message = "请输入你的兴趣爱好")  @Size(max = 5, message = "兴趣爱好最多可以输入5个")  private List<String> hobbies;  @DecimalMin(value = "50", inclusive = false, message = "体重必须大于50KG")  private BigDecimal weight;  @Validated@NotNull(message = "请输入地址信息")  private UserAddressRequestVO address;  
}
2.定义请求接口
@RestController  
@RequestMapping("user")  
@Validated  
@Slf4j  
public class UserController {  /**  * 创建用户  * @param requestVO  * @return  */  @PostMapping("create")  public ResultResponse<Void> createUser(@Validated @RequestBody UserCreateRequestVO requestVO){  return ResultResponse.success(null);  }  /**  * 校验用户邮箱是否合法  * @param email  * @return  */  @GetMapping("email")  public ResultResponse<Void> validUserEmail(@Email(message = "邮箱格式不正确") String email){  return ResultResponse.success(null);  }
}
3.测试
  • 创建用户校验,Json请求体校验

我们需要捕获一下MethodArgumentNotValidException。该部分内容请参考文章:SpringBoot统一异常处理

  • 校验邮箱,单参数校验

注:单参数校验时我们需要,在方法的类上加上@Validated注解,否则校验不生效。

嵌套对象的校验

UserCreateRequestVO中增加一个address的校验,即需要对嵌套对象进行校验

@Data  
public class UserAddressRequestVO {  @Size(max = 16, message = "地址信息中国家长度不能超过16个字符")  @NotBlank(message = "地址信息国家不能为空")  private String country;  private String city;  @Size(max = 128, message = "详细地址长度不能超过128个字符")  private String address1;  
}

UserAddressRequestVO中增加address属性

@Data  
public class UserCreateRequestVO {@NotNull(message = "请输入地址信息")  private UserAddressRequestVO address;
}

解决办法,要在嵌套对象上使用 @Valid 注解

@Data  
public class UserCreateRequestVO {@NotNull(message = "请输入地址信息")  @Valid  private UserAddressRequestVO address;
}

自定义验证注解

在项目开发中,我们也可以自定义注解去完成我们的字段校验,比如某些枚举值的传递,需要校验枚举值是否合法。在创建自定义注解之前,我们需要了解一下ConstraintValidator以及实现自定义验证注解的原理

1.ConstraintValidator 接口

ConstraintValidator 是 Java Bean Validation (JSR 380) 规范中用于自定义验证逻辑的接口。它允许你定义针对特定自定义注解的验证规则。它是一个泛型接口,需要提供两个类型参数:

  • A:是你的自定义注解的类型。
  • T:是被验证的元素类型,通常是字段类型。
public interface ConstraintValidator<A extends Annotation, T> {void initialize(A constraintAnnotation);boolean isValid(T value, ConstraintValidatorContext context);
}

其中:

  • initialize 方法:在验证器初始化时被调用,可以用于获取约束注解中的配置信息。
  • isValid 方法:执行实际的验证逻辑,返回 true 表示验证通过,false 表示验证失败。

以下为枚举校验注解的校验规则实现

public class EnumValidator implements ConstraintValidator<EnumValid, Object> {  private Class clazz;  private String validField;  @Override  public void initialize(EnumValid constraintAnnotation) {  clazz = constraintAnnotation.enumClass();  validField = constraintAnnotation.field();  }  @SneakyThrows  @Override  public boolean isValid(Object object, ConstraintValidatorContext constraintValidatorContext) {  if (object == null || "".equals(object)){  return true;  }  if (!clazz.isEnum()){  return false;  }  Class<Enum> enumClass = (Class<Enum>)clazz;  //获取所有枚举实例  Enum[] enumConstants = enumClass.getEnumConstants();  // 需要比对的字段  Field field = enumClass.getDeclaredField(validField);  field.setAccessible(true);  for(Enum constant : enumConstants){  // 取值final修饰  Object validValue = field.get(constant);  if (validValue == null){  Method method = enumClass.getMethod(validField);  validValue = method.invoke(constant);  }  if(validValue instanceof Number) {  validValue = ((Number)validValue).intValue();  object = ((Number) object).intValue();  }  if (Objects.equals(validValue,object)){  return true;  }  }  return false;  }  
}
2.创建自定义注解

在 Java Bean Validation 中,约束注解(Constraint Annotation)是通过元注解 @Constraint 来定义的。这个注解包含了以下关键元素:

  • validatedBy: 指定用于执行验证的 ConstraintValidator 实现类。

以校验枚举值的合法行为例,我们创建一个EnumValid约束注解

@Constraint(validatedBy = {EnumValidator.class})  
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })  
@Retention(RUNTIME)  
public @interface EnumValid {  /**  * 不合法时 抛出异常信息  */  String message() default "值不合法";  /**  * 校验的枚举类  * @return  */  Class enumClass() default Enum.class;  /**  * 对应枚举类中需要比对的字段  * @return  */  String field() default "code";  Class<?>[] groups() default { };  Class<? extends Payload>[] payload() default { };  
}
3.注册 ConstraintValidator

在大多数情况下,不需要手动注册 ConstraintValidator。当你使用 @Constraint(validatedBy = EnumValidator.class) 注解时,Java Bean Validation 的实现框架会自动发现并注册相应的验证器。但在一些特殊情况下,你可能需要将验证器注册为 Spring 组件或手动配置。比如

  • 需要使用 Spring 管理的组件:  如果你的验证器需要依赖于 Spring 管理的组件(例如,使用 @Autowired 注解注入其他 bean),那么你可能需要将验证器注册为 Spring bean。这确保了验证器能够正确地使用 Spring 的依赖注入机制。
  • 需要通过属性文件进行配置:  如果你的验证器需要配置属性,而这些属性需要从 Spring 的 application.properties 或 application.yml 文件中获取,那么将验证器注册为 Spring bean 可以更容易地实现这一点。
  • 需要在验证器中使用 Spring AOP:  如果你希望在验证逻辑中使用 Spring AOP 切面,以便添加额外的逻辑或跟踪行为,那么将验证器注册为 Spring bean 可以让你更容易集成这些方面。
    这种方式可以运用到一些业务校验中,比如账户注册时用户名称不能重复。定义一个校验用户唯一的注解@UniqueUser
@Constraint(validatedBy = {UniqueUserValidator.class})  
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })  
@Retention(RUNTIME)  
public @interface UniqueUser {  /**  * 不合法时 抛出异常信息  */  String message() default "值不合法";  Class<?>[] groups() default { };  Class<? extends Payload>[] payload() default { };  
}

然后定义一个业务的Validator

@Slf4j  
@Component  
public class UniqueUserValidator implements ConstraintValidator<UniqueUser, UserCreateRequestVO> {  private UserRepository userRepository;  @Override  public boolean isValid(UserCreateRequestVO value, ConstraintValidatorContext context) {  final String userName = value.getUserName();  final UserDO userDO = userRepository.selectUserByName(userName);  final String userId = value.getUserId();  if (StringUtils.isBlank(userId)){  return userDO == null;  }  return userDO == null || Objects.equals(userDO.getUserId(), userId);  }  @Autowired  public void setUserRepository(UserRepository userRepository) {  this.userRepository = userRepository;  }  
}

在创建用户的接口中使用@UniqueUser

@RestController  
@RequestMapping("user")  
@Validated  
@Slf4j  
public class UserController {  /**  * 创建用户  * @param requestVO  * @return  */  @PostMapping("create")  public ResultResponse<Void> createUser(@Validated @UniqueUser(message = "用户名称已存在") @RequestBody UserCreateRequestVO requestVO){  return ResultResponse.success(null);  }
}

模拟当用户名存在时,校验不通过

此时会抛出javax.validation.ConstraintViolationException

4.自定义校验注解使用

我们创建一个性别的枚举类:

@AllArgsConstructor  
public enum SexEnum {  MAN(1, "男"),  WOMAN(2,"女");  public final Integer code;  public final String desc;  }

然后我们在入参中增加sex字段,并使用@EmunValid注解

@Data  
public class UserCreateRequestVO {  @NotBlank(message = "请输入用户名")  @Size(max = 128, message = "用户名长度最大为128个字符")  private String userName;  @Email(message = "请填写正确的邮箱地址")  private String email;  @Min(value = 18, message = "用户年龄必须大于18岁")  @Max(value = 60, message = "用户年龄必须小于60岁")  private Integer age;  @NotNull(message = "请输入性别")  @EnumValid(enumClass = SexEnum.class, message = "输入性别不合法")  private Integer sex;  @NotEmpty(message = "请输入你的兴趣爱好")  @Size(max = 5, message = "兴趣爱好最多可以输入5个")  private List<String> hobbies;  @DecimalMin(value = "50", inclusive = false, message = "体重必须大于50KG")  private BigDecimal weight;  
}

测试结果如下:

分组验证

在一个应用中,同一个实体类可能会被用于不同的场景,比如用户创建、用户更新、用户删除等。每个场景对于字段的要求可能不同,有些字段在某个场景下需要验证,而在另一个场景下不需要。不同的业务操作可能对同一实体的验证有不同的需求。例如,在用户创建时可能强调用户名和密码的合法性,而在用户更新时可能更关心其他信息的完整性。

开发中我们针对这种情况,在不知道分组校验的知识时,通常采取的都是对应不同的场景或者业务创建不同的入参实体,比如创建用户UserCreateRequestVO,更新用户UserUpdateRequestVO,删除用户UserDeleteRuquestVO,在不同的实体中根据业务场景设置不同的校验规则。这样做虽然也可以,但是会造成类的膨胀,业务的重复实现。

而实际上用分组校验可以让你根据场景以及业务的差异性,有选择地执行特定组的验证规则。

1.定义验证分组接口

我们定义两个分组接口CreateUserGroup(用户创建组),UpdateUserGroup(用户更新组),分别继承javax.validation.groups.Default,标识不同的业务场景。

public interface CreateUserGroup extends Default {  
}public interface UpdateUserGroup extends Default {  
}
2.分组校验的使用

在 Bean Validation 中,分组校验是通过在验证注解上指定 groups 属性来实现的。这个属性允许你为验证规则分配一个或多个验证组。我们设定用户创建时不传递用户ID,其余的参数必传,用户更新接口必须传递用户ID,可以不传递用户名,其他参数必须传递。

@Data  
public class UserCreateRequestVO {  @NotBlank(message = "请选择用户", groups = UpdateUserGroup.class)  private String userId;  @NotBlank(message = "请输入用户名", groups = CreateUserGroup.class)  @Size(max = 128, message = "用户名长度最大为128个字符")  private String userName;  @Email(message = "请填写正确的邮箱地址")  private String email;  @Min(value = 18, message = "用户年龄必须大于18岁")  @Max(value = 60, message = "用户年龄必须小于60岁")  private Integer age;  @NotNull(message = "请输入性别")  @EnumValid(enumClass = SexEnum.class, message = "输入性别不合法")  private Integer sex;  @NotEmpty(message = "请输入你的兴趣爱好")  @Size(max = 5, message = "兴趣爱好最多可以输入5个")  private List<String> hobbies;  @DecimalMin(value = "50", inclusive = false, message = "体重必须大于50KG")  private BigDecimal weight;  
}

指定了分组的校验规则,分别在对应的分组校验中生效,没有指定分组使用默认分组Default,即对所有的校验都生效。

3.在接口中使用分组

使用 @Validated 注解,并指定要执行的验证组。

@RestController  
@RequestMapping("user")  
@Validated  
@Slf4j  
public class UserController {  /**  * 创建用户  * @param requestVO  * @return  */  @PostMapping("create")  public ResultResponse<Void> createUser(@Validated(value = CreateUserGroup.class) @RequestBody UserCreateRequestVO requestVO){  return ResultResponse.success(null);  }  /**  * 更新用户  * @param requestVO  * @return  */  @PostMapping("update")  public ResultResponse<Void> updateUser(@Validated(value = UpdateUserGroup.class) @RequestBody UserCreateRequestVO requestVO){  return ResultResponse.success(null);  }
}

我们指定create接口指定CreateUserGroup分组,update接口指定UpdateUserGroup

测试接口如下:

  • 创建用户create接口
    因为userId可以不传递,接口可以校验通过

  • 更新用户update接口
    因为必须传递userId, 我们不传时校验不通过,提示选择用户

传递userId,不传递userName时,校验通过

处理验证错误

由上述测试结果中,可以看出接口抛出的一场结果并不是很友好,我们需要统一的处理一下异常以及返回结果,给予用户友好提示。

总结

Spring Boot Validation通过简化验证流程、集成Bean Validation规范、支持分组验证以及提供友好的错误处理,为Java应用开发者提供了强大而灵活的数据验证机制。最佳实践包括在控制器层使用@Validated注解、合理利用各种验证注解、使用自定义验证注解解决特定业务需求,确保代码清晰简洁、符合规范,并提高系统的可维护性和用户体验。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.luyixian.cn/news_show_925525.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

网络文件共享服务

目录 一、网络文件共享服务原理内容 1.存储类型 2.应用场景 3.总结 二、FTP——文件传输协议 1.工作原理介绍 2.vsftpd软件 2.1使用ftp 2.2延伸——FileZilla 2.3修改默认端口号 2.4主动模式端口 2.5被动模式端口 2.6匿名用户登录 2.7匿名用户上传 2.8匿名用户…

5文件操作

包含头文件<fstream> 操作文件三大类&#xff1a; ofstream : 写文件ifstream &#xff1a;读文件fstream : 读写文件 5.1文本文件 -文件以ascii的形式存储在计算机中 5.1.1写文件 步骤&#xff1a; 包含头文件 #include "fstream"创建流对象 ofs…

Open CASCADE学习|参数化球面的奇异性

参数曲面的奇异性是一个相对复杂的概念&#xff0c;它涉及到参数曲面的几何特性和参数化过程中的一些特殊情况。参数曲面通常用于描述三维空间中的复杂形状&#xff0c;通过参数方程将二维参数域映射到三维空间中。然而&#xff0c;在某些情况下&#xff0c;参数曲面可能会表现…

ssm基于vue的儿童教育网站的设计与实现论文

摘 要 传统信息的管理大部分依赖于管理人员的手工登记与管理&#xff0c;然而&#xff0c;随着近些年信息技术的迅猛发展&#xff0c;让许多比较老套的信息管理模式进行了更新迭代&#xff0c;视频信息因为其管理内容繁杂&#xff0c;管理数量繁多导致手工进行处理不能满足广大…

react 项目结构配置

1 项目整体目录结构的搭建 如下图&#xff1a; 2 重置css样式: normalize.css reset.less ; 第一步 安装 npm i normalize.css 入口文件index.tsx导入&#xff1a;import ‘noremalize.css’ 第二步 创建自己的css样式&#xff1a;在assets文件夹中创建css…

Go 知多少?

作为一名已接触过其他语言的开发&#xff0c;再去学习一门新语言可比之前轻松不少&#xff0c; 语言之间存在很多相似点&#xff0c;但是新语言也有自己的不同点&#xff0c;通常我会先了解它与其他语言常遇到的不同点有哪些&#xff0c; 使自己先能够上手编写基础程序&#…

深入理解 Spark(一)spark 运行模式简介与启动流程源码分析

spark 的运行模式 standalone 模式 以 standalone-client 为例&#xff0c;运行过程如下&#xff1a; SparkContext 连接到 Master&#xff0c;向 Master 注册并申请资源&#xff08;CPU Core 和 Memory&#xff09;&#xff1b;Master 根据 SparkContext 的资源申请要求和 …

ruoyi后台管理系统部署-4-安装nginx

yum 安装 ngix 1.24 yum 官方源安装&#xff1a; # 1. 需要预先安装 yum-utils sudo yum install yum-utils # 2. 配置yum repo touch /etc/yum.repos.d/nginx.repongix.repo: [nginx-stable] namenginx stable repo baseurlhttp://nginx.org/packages/centos/$releasever/$…

ETF交易好不好?如何选择一个好的ETF基金?

作为普通投资者&#xff0c;交易ETF基金时&#xff0c;拥有证券账户即可&#xff0c;如同买卖股票一样操作&#xff0c;非常便捷&#xff0c;可以在盘中实时买卖。和投资股票相比较&#xff0c;风险相对较低。 首先&#xff0c;什么是ETF基金? ETF其实是一个缩写&#xff1a…

41k+ stars 闪电般快速的开源搜索引擎 docker安装教程

目录 1.下载 2.启动 成功示例 3.创建索引 4.插入数据 4.1下载数据 4.2插入数据 4.3查看数据 5.官方地址 1.下载 docker pull getmeili/meilisearch:latest 2.启动 mkdir -p /opt/meili_datadocker run -it --rm \-p 7700:7700 \-v /opt/meili_data:/meili_data \ge…

AI绘画学起来真的那么难吗?附全网最全教程!!

手把手教你入门绘图超强的AI绘画&#xff0c;用户只需要输入一段图片的文字描述&#xff0c;即可生成精美的绘画。给大家带来了全新保姆级教程资料包 &#xff08;文末可获取&#xff09; 随着2022年末ChatGPT的爆火&#xff0c;人们终于不能再忽视AI的强大了&#xff0c;AI绘…

Android studio RecyclerView 应用设计

一、创建empty activity项目: 二、打开activity_main.xml布局文件: 添加RecyclerView控件 <?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/…

浅谈对Maven的理解

一、什么是Maven Maven——是Java社区事实标准的项目管理工具&#xff0c;能帮你从琐碎的手工劳动中解脱出来&#xff0c;帮你规范整个组织的构建系统。不仅如此&#xff0c;它还有依赖管理、自动生成项目站点等特性&#xff0c;已经有无数的开源项目使用它来构建项目并促进团队…

【LabVIEW FPGA 编程入门】使用FPGA IO进行编程

1.在项目中新建一个VI&#xff0c;命名为FPGA IO Test。 2. 可以直接将项目中的FPGA IO拖入程序框图中。 FPGA IO的类型&#xff1a; 数字线&#xff1a; 数字端口&#xff1a; 模拟IO&#xff1a; 其他&#xff1a; 3.如果新增加了FPGA资源&#xff0c;不是创建项目时扫描到的…

C++算法学习心得五.二叉树(3)

1.合并二叉树&#xff08;617题&#xff09; 题目要求&#xff1a; 给定两个二叉树&#xff0c;想象当你将它们中的一个覆盖到另一个上时&#xff0c;两个二叉树的一些节点便会重叠。 你需要将他们合并为一个新的二叉树。合并的规则是如果两个节点重叠&#xff0c;那么将他们…

用Python做一个2048小游戏

文章目录 逻辑设计绘图逻辑主循环 逻辑设计 2048的逻辑无非是操作 4 4 4\times4 44的方格&#xff0c;每个方格中有一个数&#xff0c;这些数可以移动&#xff0c;如果两个相同的数字在移动时相撞了&#xff0c;就可以彼此合并。 而这个 4 4 4\times4 44的方格&#xff0c;…

Jenkins自动化部署docker

Jenkins自动化部署docker和普通方式构建 docker外挂目录 准备测试服务器docker环境准备jdk环境将上传jar包修改为app.jar对外暴露1000端口启动jar FROM openjdk:8-jdk-alpine ARG JAR_FILE COPY ${JAR_FILE} app.jar EXPOSE 1000 ENTRYPOINT ["java","-jar&q…

搭建知识付费小程序平台:如何避免被坑,选择最佳方案?

随着知识经济的兴起&#xff0c;知识付费已经成为一种趋势。越来越多的人开始将自己的知识和技能进行变现&#xff0c;而知识付费小程序平台则成为了一个重要的渠道。然而&#xff0c;市面上的知识付费小程序平台琳琅满目&#xff0c;其中不乏一些不良平台&#xff0c;让老实人…

(学习日记)2024.01.09

写在前面&#xff1a; 由于时间的不足与学习的碎片化&#xff0c;写博客变得有些奢侈。 但是对于记录学习&#xff08;忘了以后能快速复习&#xff09;的渴望一天天变得强烈。 既然如此 不如以天为单位&#xff0c;以时间为顺序&#xff0c;仅仅将博客当做一个知识学习的目录&a…

CSS 下载进度条

<template><view class=btn>下载中</view></template><script></script><style>/* 设置整个页面的样式 */body {width: 100vw; /* 页面宽度为视口宽度 */background: #000000; /* 背景颜色为白色 */display: flex; /* 使用 flex…