当前位置: 首页 > news >正文

Spring Boot——优雅的参数校验

🎈 概述

当我们想提供可靠的 API 接口,对参数的校验,以保证最终数据入库的正确性,是 必不可少 的活。比如下图就是 我们一个项目里 新增一个菜单校验 参数的函数,写了一大堆的 if else 进行校验,或者基础校验,如非空校验、长度校验、大小校验、格式校验;也有一些校验是业务校验,如学号不能重重复、手机号不能重复注册等,非常的不优雅,比起枯燥的CRUD来说,参数校验更是枯燥。例:

  /** * 参数校验* @param user* @return void * @author PuWenshuo* @date 2023/4/26 11:08*/ private void verifyForm(SysUser user){if(StringUtils.isBlank(user.getUserName())){throw new RuntimeException("用户名称不能为空");}if(user.getDeptId() == null){throw new RuntimeException("用户部门ID不能为空");}if(StringUtils.isBlank(user.getAvatar())){throw new RuntimeException("用户头像不能为空");}if(StringUtils.isBlank(user.getEmail())){throw new RuntimeException("用户邮箱不能为空");}if(StringUtils.isBlank(user.getPhonenumber())){throw new RuntimeException("用户手机号不能为空");}if(StringUtils.isBlank(user.getPassword())){throw new RuntimeException("用户密码不能为空");}}

🎈 引入依赖

在 Spring Boot 体系中,也提供了 spring-boot-starter-validation 依赖。在这里,我们并没有引入。为什么呢?因为 spring-boot-starter-web 已经引入了 spring-boot-starter-validation ,而 spring-boot-starter-validation 中也引入了 hibernate-validator 依赖,所以无需重复引入。

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

🎈 注解

引入依赖后,可以看到在javax.validation.constraints 包下,定义了一系列的约束( constraint )注解。共 22个,如下:
在这里插入图片描述

💧 空和非空检查

  • @Null:被注释的元素必须为 null。
  • @NotNull:被注释的元素必须不为 null。
  • @NotEmpty:被注释的字符串的必须非空,集合对象的元素不为 0 ,即集合不为空。
  • @NotBlank:只能用于字符串不为 null ,并且字符串 trim()以后 length 要大于 0 。

💧 数值检查

  • @DecimalMax(value) :被注释的元素必须是一个数字,其值必须小于等于指定的最大值。
  • @DecimalMin(value) :被注释的元素必须是一个数字,其值必须大于等于指定的最小值。
  • @Digits(integer, fraction) :被注释的元素必须是一个数字,其值必须在可接受的范围内。
  • @Positive :判断正数。
  • @PositiveOrZero :判断正数或 0 。
  • @Max(value) :该字段的值只能小于或等于该值。
  • @Min(value) :该字段的值只能大于或等于该值。
  • @Negative :判断负数。
  • @NegativeOrZero :判断负数或 0 。

💧 Boolean 值检查

  • @AssertFalse :被注释的元素必须为 true 。
  • @AssertTrue :被注释的元素必须为 false 。

💧 长度检查

  • @Size(max, min) :检查该字段的 size 是否在 min 和 max 之间,可以是字符串、数组、集合、Map 等。

💧 日期检查

  • @Future :被注释的元素必须是一个将来的日期。
  • @FutureOrPresent :判断日期是否是将来或现在日期。
  • @Past :检查该字段的日期是在过去。
  • @PastOrPresent :判断日期是否是过去或现在日期。

💧 其它检查

  • @Email :被注释的元素必须是电子邮箱地址。
  • @Pattern(value) :被注释的元素必须符合指定的正则表达式。

💧 @Valid 和 @Validated

@Valid 注解,是 Bean Validation 所定义,可以添加在普通方法、构造方法、方法参数、方法返回、成员变量上,表示它们需要进行约束校验。

@Validated 注解,是 Spring Validation 锁定义,可以添加在类、方法参数、普通方法上,表示它们需要进行约束校验。同时,@Validated 有 value 属性,支持分组校验。属性如下:
在这里插入图片描述
@Validjavax.validation包下) 和 @Validatedorg.springframework.validation.annotation包下)注解。两者大致有以下的区别:

名称是否实现声明式校验是否支持嵌套校验是否支持分组校验
@Validfalsetruefalse
@Validatedtruefalsetrue

绝大多数场景下,我们使用 @Validated 注解即可。而在有嵌套校验的场景,我们使用 @Valid 注解添加到成员属性上。

🎈 基本使用

参数校验分为简单校验、嵌套校验、分组校验。

💧 简单校验

简单校验直接在需要的实体类元素中标注约束注解即可。例如:

// 在实体类中添加各种不同的校验注解
@Data
public class UserRegisterDTO {@NotBlank(message = "用户名不能为空")private String username;@NotBlank(message = "密码不能为空")@Pattern(regexp = "^[0-9a-zA-Z]{6,16}$", message = "密码必须为 6-16 位的数字字母组合")private String password;@Email(message = "邮箱格式不正确")private String email;
}

注:同一个属性可以指定多个约束,比如@NotBlank和@Pattern,其中的message属性指定了约束条件不满足时的提示信息。

以上约束标记完成之后,要想完成校验,需要在controller层的接口标注@Valid注解即可。如下所示:

@RestController
@RequestMapping("/user")
public class UserController {@PostMapping("/register")public Result register(@RequestBody @Valid  UserRegisterDTO userRegisterDTO) {// 参数校验通过后的逻辑处理}}

💧 嵌套校验

在 Spring Boot 中,我们可以使用注解实现嵌套校验。嵌套校验主要是针对复杂的业务场景,例如一个对象中包含了另一个对象,或者一个集合中包含了多个元素等情况。

嵌套校验很简单,只需要在嵌套的实体属性标注@Valid注解,则其中的属性也将会得到校验,否则不会校验。

例如下面的实体类校验:

// 用户实体类
@Data
public class User {@NotNull(message = "用户名不能为空")private String username;@Size(min = 6, max = 20, message = "密码长度应在 6 到 20 之间")private String password;
}// 商品实体类
@Data
public class Product {@NotNull(message = "商品编号不能为空")private String productId;@NotEmpty(message = "商品名称不能为空")private String productName;@Positive(message = "商品价格必须为正数")private BigDecimal price;
}// 订单实体类  ,需要使用@Valid标注才能嵌套校验用户类和商品类
@Data
public class Order {@NotNull(message = "订单号不能为空")private String orderId;@Valid@NotNull(message = "订单用户信息不能为空")private User user;/*** @Valid这个注解标注在集合上,将会针对集合中每个元素进行校验*/@Valid@Size(min = 1,message = "至少一个商品")@NotEmpty(message = "订单商品列表不能为空")private List<Product> productList;
}

则在Controller层的接口,需要使用@Valid或者@Validated标注入参,嵌套校验中仍可以使用分组校验。

@RestController
@RequestMapping("/order")
public class OrderController {@PostMapping("/add")public void addOrder(@RequestBody @Valid  Order order) {// 业务处理}}

总结:嵌套校验只需要在需要校验的元素(单个或者集合)上添加@Valid注解,接口层需要使用@Valid或者@Validated注解标注入参。

💧 分组校验

有的时候,开发者在某一个实体类中定义了很多校验规则,但是在某一次业务处理中,并不需要这么多校验规则,此时就可以使用分组校验:
首先创建两个分组接口:

public interface AddGroup {}public interface UpdateGroup {}

在实体类中添加分组信息:

@Data
public class User {/*** groups属性,表示该校验属性规则所属的分组*/@NotNull(message = "用户名不能为空", groups = {UpdateGroup.class})private String id;@NotNull(message = "用户名不能为空", groups = {AddGroup.class, UpdateGroup.class})private String username;@Size(min = 6, max = 20, message = "密码长度应在 6 到 20 之间", groups = {AddGroup.class})private String password;@Email(message = "邮箱格式不正确")private String email;
}

在在Controller层的接口使用@Validated注解中指定校验分组:

@RestController
@RequestMapping("/user")
public class UserController {@PostMapping("/add")public void addUser(@RequestBody @Validated(AddGroup.class) User user) {// 业务处理}/*** @Validated({UpdateGroup.class, Default.class})表示UpdateGroup和默认分组都参与校验*/@PostMapping("/update")public void updateUser(@RequestBody @Validated({UpdateGroup.class, Default.class}) User user) {// 业务处理}}

💧 @RequestBody注解参数

在 Spring Boot 中,我们可以使用 @RequestBody 注解来接收请求体中的参数,并对其进行校验。通常情况下,我们会使用 javax.validation.constraints 包提供的注解来对请求参数进行校验。
下面是一个示例,展示如何使用 @RequestBody 注解和校验注解来对请求参数进行校验:

@RestController
public class UserController {@PostMapping("/user")public User addUser(@Validated @RequestBody User user) {return user;}}

在上面的例子中,我们定义了一个 addUser() 方法,并使用 @RequestBody 注解来接收请求体中的参数,这里的参数类型是 User 类型。然后,我们使用 @Validated 注解来告诉 Spring Boot 对请求参数进行校验,同时,在 User 类中使用校验注解进行参数校验,例如 @NotBlank、@NotNull、@Min、@Max 等等。

举个例子,假设我们的 User 类如下所示:

@Data
public class User {@NotBlank(message = "用户名不能为空")private String username;@NotBlank(message = "密码不能为空")private String password;@Email(message = "邮箱格式不正确")private String email;
}

在上面的例子中,我们在 User 类中添加了几个校验注解来校验请求参数的合法性,例如 @NotBlank 来判断参数是否为空或空格,@Email 来判断邮箱格式是否正确等等。

当客户端发起 POST 请求时,请求体中包含一个合法的 User 对象,Spring Boot 会自动对其进行校验。如果请求参数不符合规范,则会抛出相应的异常(例如 MethodArgumentNotValidException),并返回给客户端相应的错误信息。

总结

  1. 在controller层的方法的形参数前面加一个@Valid或@Validated的注解;
  2. 在用@RequestBody修饰的类的属性上加上约束注解,如@NotNull、@Length、@NotBlank;
  3. @RequestBody参数在触发校验规则时,会抛出MethodArgumentNotValidException,这里使用统一的异常处理机制来处理异常;

💧 @RequestParam注解/@PathVariable注解参数

在 Spring Boot 中,如果需要对 @RequestParam 参数和 @PathVariable 参数进行校验,我们通常会使用 javax.validation.constraints 提供的注解来实现。如果我们在控制类中使用注解进行参数校验,那么我们需要在该类上添加 @Validated 注解来开启校验功能。

例如:

@RestController
@Validated
public class UserController {@GetMapping("/user/{id}")public User getUser(@PathVariable("id") @Min(1) Long id) {// 业务逻辑}@PostMapping("/user")public void addUser(@RequestParam("username") @NotBlank String username, @RequestParam("password") @Size(min = 6, max = 20) String password) {// 业务逻辑}}

在上面的例子中,我们在 @PathVariable 和 @RequestParam 参数上分别添加了校验注解,例如 @Min、@NotBlank、@Size 等等。同时,我们在控制类上添加了 @Validated 注解来开启校验功能。

需要注意的是,在使用 @Validated 注解时,我们应该将其添加在控制类的上方,而不是方法上方。这是因为,当我们在方法上方添加 @Validated 注解时,将只会对该方法的参数进行校验,而不会校验控制类中其他方法的参数。

总之,如果需要对 @RequestParam 参数和 @PathVariable 参数进行校验,我们可以在控制类中使用校验注解来实现,并在控制类上添加 @Validated 注解开启校验功能。如果校验失败,则 Spring 会抛出 MethodArgumentNotValidException 异常,并将错误信息封装成 BindingResult 对象返回给控制层。

🎈 异常处理

既然是校验参数,有校验通过自然也就有不通过,如果参数校验失败,框架会自动抛出异常。对于异常可以通过两种方式处理:BindingResult 接收和全局捕获异常

💧 BindingResult 接收

在 Spring Boot 中,校验注解对请求参数进行校验时,当校验失败时,框架会将错误信息绑定到 BindingResult对象中,并将该对象作为方法参数传递给控制器方法。我们可以通过BindingResult对象获取到校验结果和错误信息,并进行相应的处理。

下面是一个示例,展示如何使用 BindingResult 对象接收参数校验异常:

@RestController
public class UserController {@PostMapping("/user")public void addUser(@Validated @RequestBody User user, BindingResult result) {if (result.hasErrors()) {List<String> errorMessages = new ArrayList<>();for (FieldError fieldError : result.getFieldErrors()) {String errorMessage = String.format("%s:%s", fieldError.getField(), fieldError.getDefaultMessage());errorMessages.add(errorMessage);}throw new RuntimeException(StringUtils.join(errorMessages, ";"));}// 接口正常的业务逻辑}}

在上面的例子中,我们定义了一个控制器方法 addUser,用来添加用户信息。我们在方法参数前添加了 @Validated 注解,表示需要对该参数进行校验。同时,我们还在方法的参数列表中添加了 BindingResult 参数,用来接收校验结果和错误信息。

在方法体内,我们首先使用 result.hasErrors() 判断是否存在校验错误。如果存在校验错误,我们遍历 FieldError 集合获取每个参数的校验错误信息,并将其封装到一个字符串列表 errorMessages 中。最后,我们将所有错误信息使用分号 ; 连接起来,并通过抛出运行时异常的方式将错误信息抛出。

需要注意的是,当我们使用 BindingResult 接收校验结果时,我们应该提供明确的错误提示信息,并避免返回给客户端过于详细的错误信息,以保障代码的安全性。

优点:使用 BindingResult 对象接收参数校验异常是一种比较简单和直接的方式,可以有效地处理参数校验异常,提高代码的健壮性和可靠性。

缺点

  1. 当控制器方法参数列表中添加了 BindingResult 参数时,如果校验出错,框架不会抛出异常,而是将错误信息保存在 BindingResult 对象中返回到控制器方法中。因此,在处理校验结果时,我们需要手动判断是否存在校验错误,并获取校验错误信息。这会增加代码的复杂程度和维护成本。
  2. 使用 BindingResult 对象进行参数校验时,错误信息可能不够明确或不够详细,导致用户难以理解校验错误原因,从而无法快速定位问题所在。特别是当参数校验规则变得复杂时,错误信息可能不够准确或直观,使得调试和排查问题变得更加困难。
  3. 在使用 BindingResult 对象进行参数校验时,需要手动编写错误处理逻辑,并对校验结果进行详细的记录和分析,这会增加开发者的负担和工作量。

综上所述,虽然使用 BindingResult 对象进行参数校验可以有效地处理参数校验异常,但是它也存在一些缺点,例如代码复杂度高、错误信息不够明确等等。因此,在实际应用中,我们可以根据具体情况选择不同的方式来处理参数校验异常。如果需要更精准和明确的错误信息,可以使用 Spring Boot 中的全局异常处理器来处理参数校验结果。

💧 全局捕获异常处理

参数在校验失败的时候会抛出的MethodArgumentNotValidException或者BindException两种异常,可以在全局的异常处理器中捕捉到这两种异常,将提示信息或者自定义信息返回给客户端。

下面是一个示例,展示如何在 Spring Boot 中处理参数校验结果:

/*** 全局异常处理器* * @author Wen先森*/
@RestControllerAdvice
public class GlobalExceptionHandler
{private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);/*** 自定义验证异常*/@ExceptionHandler(BindException.class)public AjaxResult validatedBindException(BindException e){log.error(e.getMessage(), e);// 这里我只取了第一个错误信息。String message = e.getAllErrors().get(0).getDefaultMessage();// 同意返回前端错误信息处理return AjaxResult.error(message);}/*** 自定义验证异常*/@ExceptionHandler(MethodArgumentNotValidException.class)public Object validExceptionHandler(MethodArgumentNotValidException e){log.error(e.getMessage(), e);// getFieldError()不指定参数,会默认取第一个错误信息。String message = e.getBindingResult().getFieldError().getDefaultMessage();return AjaxResult.error(message);}
}

Spring Boot 提供了全局异常处理机制,可以通过 @ControllerAdvice 和 @ExceptionHandler 注解来处理控制器或服务中发生的异常。在上面的例子中,我们定义了一个全局异常处理器 GlobalExceptionHandler,用来捕获 MethodArgumentNotValidException 异常和BindException异常。e.getBindingResult()是获取到校验结果和错误信息即BindingResult

getFieldErrors()getFieldError() 都是 Spring Boot 中用于获取校验错误信息的方法,但是它们的返回值和使用场景略有不同。
getFieldErrors() 方法可以获取到所有校验失败的错误信息,返回的是一个 List 集合,其中每个元素表示一个参数的校验错误信息。例如,我们可以使用以下代码获取到所有校验失败的参数信息:

BindingResult result = ... // 获取到绑定结果对象
List<FieldError> fieldErrors = result.getFieldErrors();
for (FieldError fieldError : fieldErrors) {String fieldName = fieldError.getField(); // 获取参数名String errorMsg = fieldError.getDefaultMessage(); // 获取错误信息// ...
}

getFieldErrors() 方法通常用来获取到所有校验失败的信息,并在控制器或服务中做进一步处理。例如,我们可以将所有的错误信息封装成一个响应对象并返回给客户端。

而 getFieldError() 方法则只能获取到某个具体参数的校验错误信息,返回的是一个 FieldError 对象,该对象包含了参数名、错误信息等详细信息。例如,我们可以使用以下代码获取到某个具体参数的校验错误信息:

BindingResult result = ... // 获取到绑定结果对象
FieldError fieldError = result.getFieldError("age"); // 获取 "age" 参数的错误信息
// 如果不指定参数,会默认获取第一个
// FieldError fieldError = result.getFieldError();
if (fieldError != null) {String fieldName = fieldError.getField(); // 获取参数名String errorMsg = fieldError.getDefaultMessage(); // 获取错误信息// ...
}

🎈 自定义校验

在Spring Boot中,可以通过自定义注解,并结合@Valid@Validated注解来实现自定义校验。

💧 自定义校验注解

下面是一个简单的示例,展示如何自定义一个校验注解:

@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
@Retention(RUNTIME)
@Repeatable(List.class)
@Documented
@Constraint(validatedBy = MyValidator.class)// 指定校验器
public @interface MyConstraint {String message() default "自定义校验错误信息";Class<?>[] groups() default { };Class<? extends Payload>[] payload() default { };@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })@Retention(RUNTIME)@Documented@interface List {MyConstraint [] value();}
}

这段代码定义了一个 MyConstraint(自定义注解)注解,它表示被注解的元素的值要小于等于指定的值。

注解应用于的元素类型包括:方法、字段、注解类型、构造函数、参数、类型使用。

@Retention(RUNTIME) 表示该注解在运行时可见,这样就可以使用反射机制来读取注解信息。

@Repeatable(List.class) 表示此注解可以重复标注在同一元素上,而多次使用该注解时,需要使用外部注解 List 来包装并保存多个 Max 注解的数组(具体实现请参考上面的问题解答)。

@Documented 表示该注解会包含在JavaDoc中。

注解中的属性包括:

  • String message(),用于定义校验失败时的提示信息,默认为 “自定义校验错误信息”
  • Class<?>[] groups(),用于将校验信息分组,方便给不同的校验规则分配到不同的分组中;
  • Class<? extends Payload>[] payload(),用于在校验失败时传递的一些附加信息;

最后,该注解还内部定义了一个嵌套注解 List,用于表示被注解元素可以有多个MyConstraint(自定义注解)注解,通过 List 来包装这些注解,以此实现重复注解的功能。

我们还在 @MyConstraint 注解上加了 @Constraint(validatedBy = MyValidator.class) 注解,其中,validatedBy 属性指定了一个校验器类,该类需要实现 javax.validation.ConstraintValidator 接口,用于对注解进行具体的校验逻辑。

💧 自定义校验器

自定义校验注解的目的是为了自定义校验规则,而自定义校验器则是实现这种自定义校验规则的具体方式,在自定义注解中指定自定义校验器可以使得该注解在被使用时自动调用相应的校验器进行参数校验。

上面自定义检验注解时指定了校验器为MyValidator,自定义校验器需要实现ConstraintValidator<A extends Annotation, T>这个接口,第一个泛型是校验注解,第二个是参数类型。

接下来我们实现自定义检验器:

public class MyValidator implements ConstraintValidator<MyConstraint, Object> {@Overridepublic void initialize(MyConstraint constraintAnnotation) {// 可以在这里初始化校验器}@Overridepublic boolean isValid(Object value, ConstraintValidatorContext context) {// 在这里编写具体的校验逻辑if (value == null) {return true; // 如果校验的值为null,就不进行校验,交给@NotNull等其他校验注解处理}// 假设我们要校验字符串长度是否超过10个字符String str = (String) value;return str.length() <= 10;}
}

该自定义校验器实现了 ConstraintValidator 接口,并通过泛型 ConstraintValidator<MyConstraint, Object> 指定了被校验值的类型和自定义注解类型。在实现该接口的方法中,initialize方法可以用于从自定义注解中获取注解属性,而 isValid 方法则是实际进行校验的方法,其中第一个参数是被校验的值,第二个参数是校验上下文,可以用来设置校验失败时的错误信息。

相关文章:

Spring Boot——优雅的参数校验

&#x1f388; 概述 当我们想提供可靠的 API 接口&#xff0c;对参数的校验&#xff0c;以保证最终数据入库的正确性&#xff0c;是 必不可少 的活。比如下图就是 我们一个项目里 新增一个菜单校验 参数的函数&#xff0c;写了一大堆的 if else 进行校验&#xff0c;或者基础校…...

【c语言】typedef的基本用法 | 定义格式

创作不易&#xff0c;本篇文章如果帮助到了你&#xff0c;还请点赞支持一下♡>&#x16966;<)!! 主页专栏有更多知识&#xff0c;如有疑问欢迎大家指正讨论&#xff0c;共同进步&#xff01; 给大家跳段街舞感谢支持&#xff01;ጿ ኈ ቼ ዽ ጿ ኈ ቼ ዽ ጿ ኈ ቼ ዽ ጿ…...

深度学习论文分享(二)Data-driven Feature Tracking for Event Cameras

深度学习论文分享&#xff08;二&#xff09;Data-driven Feature Tracking for Event Cameras&#xff08;CVPR2023&#xff09; 前言Abstract1. Introduction2. Related Work3. Method3.1. Feature Network3.2. Frame Attention Module3.3. Supervision 4. Experiments5. Con…...

蛇优化算法

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 蛇优化算法算法流程图初始化进化操作搜索阶段&#xff08;无食物&#xff09;——全局搜索搜索阶段&#xff08;有食物&#xff09;——局部搜索战斗模式交配模式 备…...

循环神经网络(RNN)简单介绍—包括TF和PyTorch源码,并给出详细注释

文章目录 循环神经网络&#xff08;RNN&#xff09;入门教程1. 循环神经网络的原理2. 循环神经网络的应用3. 使用keras框架实现循环神经网络3.1导入对应的库及加载数据集3.2.数据预处理3.3定义RNN模型3.4训练模型3.5测试模型 4.使用PyTorch框架实现上述功能—注释详细5.结论 循…...

Struts2 快速入门

Struts2 是一个基于 MVC 设计模式的 Java Web 应用程序框架&#xff0c;它可以帮助我们更加有效地开发 Web 应用程序。Struts2 采用了前端控制器模式&#xff0c;通过核心控制器 DispatchServlet 将所有请求进行集中处理&#xff0c;然后将请求分发到指定的 Action 中&#xff…...

关于PullToRefreshView下拉刷新失效问题

一、问题原因 昨天&#xff0c;突然一个问题丢在了我的头上&#xff0c;用户反馈说某某界面下拉刷新不好使啊&#xff0c;怎么回事。二话不说直接运行项目&#xff0c;经过测试&#xff0c;发现果然不好使。一看代码提交日期好家伙2020年&#xff0c;百思不得其解&#xff0c;…...

JAVA开发中的六大原则

JAVA开发中的六大原则&#xff0c;也被称为SOLID原则&#xff0c;是软件开发中常用的一组设计原则。这些原则提供了实现高质量、易于维护和可扩展软件的基本策略。 以下是JAVA开发中的六大原则以及它们的详细说明&#xff1a; 单一职责原则&#xff08;Single Responsibility…...

Matplotlib 安装

Matplotlib 安装 本章节&#xff0c;我们使用 pip 工具来安装 Matplotlib 库&#xff0c;如果还未安装该工具&#xff0c;可以参考python 怎么使用pip进行包管理。 安装 matplotlib 库&#xff1a; pip install matplotlib 安装完成后&#xff0c;我们就可以通过 import 来…...

CF - Li Hua and Pattern

题意&#xff1a;给出了矩阵&#xff0c;里面每个位置分为蓝色或红色&#xff08;数据上用1和0体现了&#xff09;&#xff0c;给出了一个操作次数&#xff0c;每次可以改变一个坐标的颜色&#xff0c;问能否通过操作使得图像旋转180度后不变。 解&#xff1a;很容易想到&…...

重磅!阿里云云原生合作伙伴计划全新升级:加码核心权益,与伙伴共赢新未来

在今天的 2023 阿里云合作伙伴大会上&#xff0c;阿里云智能云原生应用平台运营&生态业务负责人王荣刚宣布&#xff1a; “阿里云云原生合作伙伴计划”全新升级。他表示&#xff1a; 云原生致力于帮助企业客户最大限度的减轻运维工作&#xff0c;更好的实现敏捷创新&#x…...

OSCP-Escape(gif绕过)

目录 扫描 WEB 扫描 sudo nmap 192.168.233.113 -p- -sS -sVPORT STATE SERVICE VERSION 22/tcp open ssh OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0) 80/tcp open http Apache httpd 2.4.29 ((Ubuntu)) 8080/tcp open http Apache…...

iMazing2023最新免费版iOS设备管理软件

iMazing是一款功能强大的iOS设备管理软件&#xff0c;它可以帮助用户备份和管理他们的iPhone、iPad或iPod Touch上的数据。除此之外&#xff0c;它还可以将备份数据转移到新的设备中、管理应用程序、导入和导出媒体文件等。本文将详细介绍iMazing的功能和安全性&#xff0c;并教…...

Git上传文件代码到GitHub

删除线上git:删除GitHub仓库里的文件夹_阿巴资源站的博客-CSDN博客 1. 创建空文件夹 2. cd 到本文件夹 3. git init &#xff0c;初始化&#xff0c;在本地创建一个Git仓库 4. 同时按住“ Command Shift . ”三个按键&#xff0c;即可查看当下文件夹中的隐藏文件 5. 克隆…...

JavaScript概述二(Date+正则表达式+Math+函数+面向对象)

1.Date 1.1 new一个Date对象表示当前系统时间 var nownew Date(); console.log(now);1.2 根据传入的时间格式表示时间 var date1new Date(2023-4-20 00:16:40); console.log(date1); 1.3 传入时间毫秒数&#xff0c;返回从1900年1月1日8时&#xff08;东八区&#xff09;X分X…...

一个朋友弄来的,太牛了,特别是后面内容,不看不知道,一看吓一跳,电话,热线

一个朋友弄来的&#xff0c;太牛了&#xff0c;特别是后面内容&#xff0c;不看不知道&#xff0c;一看吓一跳&#xff0c;我也收藏一下&#xff1a; 工商银行 95588 建设银行 95533 农业银行 95599 中国银行 95566 交通银行 95559 浦发银行 95528 民生银行 95568 兴业银行 955…...

VGA协议实践

文章目录 前言一、VGA接口定义与传输原理1、VGA接口定义2、传输原理3、不同分辨率对应不同参数 二、Verilog编程1、VGA显示彩色条纹2、VGA显示字符3、输出一幅彩色图像4、Quartus操作1、添加PLL核2、添加ROM核 三、全部代码四、总结五、参考资料 前言 VGA的全称是Video Graphi…...

毕业5年的同学突然告诉我,他已经是年薪30W的自动化测试工程师....

作为一名程序员&#xff0c;都会对自己未来的职业发展而焦虑。一方面是因为IT作为知识密集型的行业&#xff0c;知识体系复杂且知识更新速度非常快&#xff0c;“一日不学就会落后”。 另外一方面&#xff0c;IT又是劳动密集型的行业&#xff0c;不仅业人员多&#xff0c;而且个…...

操作系统原理 —— 进程有哪几种状态?状态之间如何切换?(七)

进程的五种状态 首先我们一起来看一下进程在哪些情况下&#xff0c;会有不同的状态表示。 创建态、就绪态 当我们刚开始运行程序的时候&#xff0c;操作系统把可执行文件加载到内存的时候&#xff0c;进程正在被创建的时候&#xff0c;它的状态是创建态&#xff0c;在这个阶…...

可算是熬出头了,测试4年,费时8个月,入职阿里,涨薪14K

前言 你的努力&#xff0c;终将成就无可替代的自己。 本科毕业后就一直从事测试的工作&#xff0c;和多数人一样&#xff0c;最开始从事点点点的工作&#xff0c;看着自己的同学一步一步往上走&#xff0c;自己还是在原地踏步&#xff0c;说实话这不是自己想要的状态。 一年半…...

业务系统对接大模型的基础方案:架构设计与关键步骤

业务系统对接大模型&#xff1a;架构设计与关键步骤 在当今数字化转型的浪潮中&#xff0c;大语言模型&#xff08;LLM&#xff09;已成为企业提升业务效率和创新能力的关键技术之一。将大模型集成到业务系统中&#xff0c;不仅可以优化用户体验&#xff0c;还能为业务决策提供…...

1688商品列表API与其他数据源的对接思路

将1688商品列表API与其他数据源对接时&#xff0c;需结合业务场景设计数据流转链路&#xff0c;重点关注数据格式兼容性、接口调用频率控制及数据一致性维护。以下是具体对接思路及关键技术点&#xff1a; 一、核心对接场景与目标 商品数据同步 场景&#xff1a;将1688商品信息…...

第一篇:Agent2Agent (A2A) 协议——协作式人工智能的黎明

AI 领域的快速发展正在催生一个新时代&#xff0c;智能代理&#xff08;agents&#xff09;不再是孤立的个体&#xff0c;而是能够像一个数字团队一样协作。然而&#xff0c;当前 AI 生态系统的碎片化阻碍了这一愿景的实现&#xff0c;导致了“AI 巴别塔问题”——不同代理之间…...

PL0语法,分析器实现!

简介 PL/0 是一种简单的编程语言,通常用于教学编译原理。它的语法结构清晰,功能包括常量定义、变量声明、过程(子程序)定义以及基本的控制结构(如条件语句和循环语句)。 PL/0 语法规范 PL/0 是一种教学用的小型编程语言,由 Niklaus Wirth 设计,用于展示编译原理的核…...

【HarmonyOS 5 开发速记】如何获取用户信息(头像/昵称/手机号)

1.获取 authorizationCode&#xff1a; 2.利用 authorizationCode 获取 accessToken&#xff1a;文档中心 3.获取手机&#xff1a;文档中心 4.获取昵称头像&#xff1a;文档中心 首先创建 request 若要获取手机号&#xff0c;scope必填 phone&#xff0c;permissions 必填 …...

Rapidio门铃消息FIFO溢出机制

关于RapidIO门铃消息FIFO的溢出机制及其与中断抖动的关系&#xff0c;以下是深入解析&#xff1a; 门铃FIFO溢出的本质 在RapidIO系统中&#xff0c;门铃消息FIFO是硬件控制器内部的缓冲区&#xff0c;用于临时存储接收到的门铃消息&#xff08;Doorbell Message&#xff09;。…...

算法打卡第18天

从中序与后序遍历序列构造二叉树 (力扣106题) 给定两个整数数组 inorder 和 postorder &#xff0c;其中 inorder 是二叉树的中序遍历&#xff0c; postorder 是同一棵树的后序遍历&#xff0c;请你构造并返回这颗 二叉树 。 示例 1: 输入&#xff1a;inorder [9,3,15,20,7…...

【Linux】Linux安装并配置RabbitMQ

目录 1. 安装 Erlang 2. 安装 RabbitMQ 2.1.添加 RabbitMQ 仓库 2.2.安装 RabbitMQ 3.配置 3.1.启动和管理服务 4. 访问管理界面 5.安装问题 6.修改密码 7.修改端口 7.1.找到文件 7.2.修改文件 1. 安装 Erlang 由于 RabbitMQ 是用 Erlang 编写的&#xff0c;需要先安…...

算术操作符与类型转换:从基础到精通

目录 前言&#xff1a;从基础到实践——探索运算符与类型转换的奥秘 算术操作符超级详解 算术操作符&#xff1a;、-、*、/、% 赋值操作符&#xff1a;和复合赋值 单⽬操作符&#xff1a;、--、、- 前言&#xff1a;从基础到实践——探索运算符与类型转换的奥秘 在先前的文…...

GeoServer发布PostgreSQL图层后WFS查询无主键字段

在使用 GeoServer&#xff08;版本 2.22.2&#xff09; 发布 PostgreSQL&#xff08;PostGIS&#xff09;中的表为地图服务时&#xff0c;常常会遇到一个小问题&#xff1a; WFS 查询中&#xff0c;主键字段&#xff08;如 id&#xff09;莫名其妙地消失了&#xff01; 即使你在…...