SpringBoot的validation参数校验

文章目录
  • 前言
  • 一、引入validation 依赖
  • 二、validation中的注解说明
    • (1)@Validated
    • (2)@Valid
    • (3)@NotNull
    • (4)@NotBlank
    • (5)@NotEmpty
    • (6)@Pattern
    • (7) @Email
    • (8)@Size
  • 三、validation的使用
    • 1.一般校验
    • 2.嵌套验证
    • 3.分组校验
    • 4.自定义校验注解
    • 5.反射加自定义注解实现动态校验
  • 四、validation 校验失败处理
  • 总结

前言

在项目开发过程,后端经常会对前端传递的参数进行各种校验,只有在校验通过时,才会执行后续的业务代码,否则抛出异常信息给前端。当参数较多时,刚开始会使用大量的 if…else…来逐一对参数进行检验,或则使用策略模式来方法来减少 if…else…的检验代码。但是将这些代码写在控制层或则业务层都会让代码过于臃肿。而且如果后期参数校验变了,或则参数不需要校验时,还需要大量删除,或修改检验参数的逻辑代码,因此采用 validation来对参数进行校验简化代码。


一、引入validation 依赖

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

二、validation中的注解说明

(1)@Validated

用于在 Spring 控制器层方法参数进行验证

@PostMapping("/validator")
public String ValidatorTest(@RequestBody @Validated UserParam user) {
     return "success";
 }

(2)@Valid

是 Java 标准的一部分,用于声明需要对嵌套对象进行递归验证。在 Spring 框架中,通常用于处理复杂对象结构的验证,如验证对象中的集合或对象引用的属性

@Data
public class EmpParam  implements Serializable {

    @NotBlank(message = "员工姓名不能为空")
    private String empName;

    @NotBlank(message = "员工手机号不能为空")
    private String empPhone;

    @Valid
    @NotNull(message = "部门信息不能为空")
    private DeptParam deptParam;
}

@Data
public class DeptParam implements Serializable {

    @NotNull(message = "部门ID不能为空")
    private Long deptId;

    @NotBlank(message = "部门名称不能为空")
    private String deptName;
}

(3)@NotNull

注解用于检查被注解的元素值不为 null。适用于字符串、集合、Map 等任何对象类型,但不适用于基本数据类型(如 int、long 等)
常用于检查对象是否为 null

@NotNull(message = "员工ID不能为空")
private Long empId;

@NotNull(message = "部门信息不能为空")
private DeptParam deptParam;

(4)@NotBlank

用于检查被注解的字符串元素不为 null 且去除两端空白字符后长度大于 0。只适用于字符串类型。

@NotBlank(message = "员工身份证号不能为空")
private String empIdCard;

(5)@NotEmpty

注解用于检查被注解的元素不为 null 且不为空,适用于字符串、集合、Map 等。
如果是字符串,相当于同时检查 null 和长度大于 0

@NotEmpty
private List<String> emails;

(6)@Pattern

指定字段必须符合指定的正则表达式

@Pattern(regexp ="^1[3|4|5|6|7|8|9][0-9]d{8}$")
private String phone

(7) @Email

指定字段必须符合Email格式。

@Email
private String email;

(8)@Size

指定字段的长度范围

@Size(min = 6, max = 20)
private String password;

三、validation的使用

1.一般校验

一般校验:这里指的是,后端参数是一个实体类对象来接收参数,并且校验实体类中各个属性(被注解标注过的)

@RestController
@RequestMapping("/test")
public class ValidatorTestController {

    @PostMapping("/validator")
    public String ValidatorTest(@RequestBody @Validated UserParam user) {

        UserParam contractParam = new UserParam();
        BeanUtils.copyProperties(user, contractParam);
        System.out.println(contractParam);
        return "success";
    }
}

实体类中的属性分别添加注解进行校验

@Data
public class UserParam {

    @NotBlank(message = "用户名不能为空")
    private String username;

    @NotBlank(message = "密码不能为空")
    @Size(min = 6, max = 20, message = "密码长度在6-20位之间")
    private String password;

    @NotBlank(message = "手机号不能为空")
    private String phone;

    @NotBlank(message = "身份证不能为空")
    private String idCard;

    @NotBlank(message = "身份证姓名不能为空")
    private String idCardName;

    @NotBlank(message = "车牌号不能为空")
    private String carNumber;

    @NotBlank(message = "银行卡号不能为空")
    private String bankCardNumber;

}

用postman测试注解是生效的。注意响应的结果,是将所有参数没校验通过都一并响应给前端,方便前端处理。
在这里插入图片描述

2.嵌套验证

嵌套检验:后端接收参数是个实体类对象,并且实体类对象中还有一个实体或者是一个List类型对象
比如:添加员工信息时,为员工分配部门
控制层一定得使用 @Validated标注参数

@RestController
@RequestMapping("/emp")
public class EmpController {
    @PostMapping("/add")
    public String addEmp(@RequestBody @Validated EmpParam empParam) {
        System.out.println(empParam);
        return "success";
    }
}

员工实体类参数:DeptParam 必须加上 @Valid注解才能校验 DeptParam 中的字段(被注解标注的)

@Data
public class EmpParam  implements Serializable {

    @NotBlank(message = "员工姓名不能为空")
    private String empName;

    @NotBlank(message = "员工手机号不能为空")
    private String empPhone;

    @NotBlank(message = "员工邮箱不能为空")
    private String empEmail;

    @NotBlank(message = "员工身份证号不能为空")
    private String empIdCard;

    @Valid
    @NotNull(message = "部门信息不能为空")
    private DeptParam deptParam;
}


@Data
public class DeptParam implements Serializable {

    @NotNull(message = "部门ID不能为空")
    private Long deptId;

    @NotBlank(message = "部门名称不能为空")
    private String deptName;
}

使用postman测试
在这里插入图片描述

3.分组校验

分组校验指的是,不通场景,参数检验方式不同。比如添加员工时候,由于empId在数据库是自增的,所以添加员工时 empParam 参数中empId 可以为空,但是在修改用户时,就要求empId不能为空。前提是 emp 新增和修改接口来接收前端的参数都是同一个 empParam 类

(1)自定义分组接口

public interface ValidationGroup {

  interface Create extends Default{}

   interface Update extends Default{}
}

(2)实体类中属性添加校验分组

@Data
public class EmpParam  implements Serializable {

    @NotNull(message = "员工ID不能为空", groups = {ValidationGroup.Update.class}) //注意查看添加接口和修改接口验证分组校验是否正确
    private Long empId;

    @NotBlank(message = "员工姓名不能为空",groups = {ValidationGroup.Create.class})
    private String empName;

    @NotBlank(message = "员工手机号不能为空")
    private String empPhone;

    @NotBlank(message = "员工邮箱不能为空")
    private String empEmail;

    @NotBlank(message = "员工身份证号不能为空")
    private String empIdCard;

    @Valid
    @NotNull(message = "部门信息不能为空")
    private DeptParam deptParam;
}

(3)控制层测试类

@RestController
@RequestMapping("/emp")
public class EmpController {
    @PostMapping("/add")
    public String addEmp(@RequestBody @Validated({ValidationGroup.Create.class}) EmpParam empParam) {
        System.out.println(empParam);
        return "success";
    }

    @PutMapping("/update")
    public String updateEmp(@RequestBody @Validated(ValidationGroup.Update.class) EmpParam empParam) {
        System.out.println(empParam);
        return "success";
    }
}

(4)使用posman测试
在这里插入图片描述

在这里插入图片描述

4.自定义校验注解

从上述方法中发现 validation这个框架所提供的校验注解,只是一些基本的判空,字符长度的校验。在日常开发中可能需要校验:比如手机号不能为空同时,必须是11位数字,以及添加用户时,用户手机号必须唯一(需要查询数据库)等等。显然是不满足日常开发的需求。因此我们可以根据 自身的需求来做校验注解。

接下来我们将自定义一个注解来专门校验身份证号

@Constraint(validatedBy = IdCardNumberValidator.class)  // @IdCardNumber 注解校验类的具体实现类
@Target({ElementType.FIELD,ElementType.METHOD}) // 表示注解可以应用于字段和方法上
@Retention(RetentionPolicy.RUNTIME) // 表示注解在运行时保留,因此可以通过反射机制读取。强调,可以通过反射获取,
public @interface IdCardNumber {

    String message() default "身份证错误";


    Class<?>[] groups() default {};


    Class<?>[] payload() default {};
}

IdCardNameValidator 校验类来实现 ConstraintValidator接口即可

import com.personal.validation.annotations.IdCardName;
import com.personal.validation.utils.ValidatorUtils;
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;

public class IdCardNameValidator implements ConstraintValidator<IdCardName, String> {
    @Override
    public void initialize(IdCardName constraintAnnotation) {
        ConstraintValidator.super.initialize(constraintAnnotation);
    }

    @Override
    public boolean isValid(String idCardName, ConstraintValidatorContext constraintValidatorContext) {
        return ValidatorUtils.isValidName(idCardName);
    }
}

校验工具类

public class ValidatorUtils {
    //判断身份证号是否有效
    /**
     * '^':表示匹配字符串的开头
     * '[1-9]':表示匹配1-9之间的数字,确保身份证号码的前6位不为0
     * '\d{5}':表示匹配5位数字,确保身份证号码的前6位为数字,用于匹配地区码
     * '(18|19|([23]\d))':表示匹配18或19或20-23之间的数字,确保身份证号码的前2位为18或19或20-23之间的数字,用于匹配年份
     * '\d{2}':表示接下来的2位是月份,范围是01-12之间的数字
     *  '((0[1-9])|(1[0-2]))':表示匹配月份,范围是01-12之间的数字
     *  '(([0-2][1-9])|10|20|30|31)':表示日期,前两位0-2表示01-29,10、20、30、31分别单独列出
     *  '\d{3}':表示匹配3位数字,确保身份证号码的第18位为数字,用于匹配顺序码
     * '[0-9Xx]':表示匹配0-9或X或x,确保身份证号码的第17位为数字或X或x,用于匹配校验位
     *
     * @param idCard
     * @return
     */
    public static boolean isValidIdCard(String idCard) {
        return Pattern.matches("^[1-9]\d{5}(18|19|([23]\d))\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$", idCard);
    }
}

实体类上加上 @IdCardNumber 注解

@Data
public class UserParam {

    @NotBlank(message = "用户名不能为空")
    private String username;

    @NotBlank(message = "密码不能为空")
    @Size(min = 6, max = 20, message = "密码长度在6-20位之间")
    private String password;

    @NotBlank(message = "手机号不能为空")
    private String phone;

    @NotBlank(message = "身份证不能为空")
    @IdCardNumber(message = "身份证不正确")// 自定义注解来校验,并不会跟原有注解冲突
    private String idCard;

    @NotBlank(message = "身份证姓名不能为空")
    private String idCardName;

    @NotBlank(message = "车牌号不能为空")
    private String carNumber;

    @NotBlank(message = "银行卡号不能为空")
    private String bankCardNumber;

}

测试自定义注解是生效的,虽然身份证没空,但是校验没能通过的自定义注解的校验类

在这里插入图片描述
以上只是简单写了一个案例,如何自定义一个注解,以后可以自定义一个注解来校验前端传递过来的参数必须在数据库是唯一的,例如手机号,不能多个用户使用同个手机号,身份证也是如此。

5.反射加自定义注解实现动态校验

现在有这样一个场景,系统中客户表中有两个类型客户:个体工商户和企业客户两类,系统的客户认证只是一个接口,但是呢,企业客户和个体客户校验信息不一样,企业校验是,企业名称,企业信用代码,法人,营业执照等。个体工商户验证时客户身份证,名字,电话等。简单来说就是通过一个 authType 认证类型 字段 来判断那些那些参数不能为空

(1)添加客户实体类参数
如果前端传递的 authType =1,就校验 SingleCustomerParam 对象不能为空,并且 SingleCustomerParam 类中被 @NotBlank注解标注也不能为空。如果authType = 2,就检验 FirmCustomerParam 对象不能为空,并且 FirmCustomerParam 类中被 @NotBlank注解标注也不能为空。如果authType != 1 或则 2 就返回认证类型错误给前端

@Data
public class AddCustomerParam implements Serializable {

    /**
     *  客户类型 1个体工商户 2:企业客户
     */
    @NotBlank(message = "客户类型不能为空")
    private String authType;

    /**
     * 1个体工商户
     */
    private SingleCustomerParam singleCustomerParam;

    /**
     * 2企业客户
     */
    private FirmCustomerParam firmCustomerParam;
}

SingleCustomerParam 实体类参数类

@Data
public class SingleCustomerParam implements Serializable {

    /**
     * 客户名称
     */
//    @NotBlank(message = "客户名称不能为空") //
    private String customerName;

    /**
     * 客户身份证照片
     */
    @NotBlank(message = "客户身份证照片不能为空")
    private String idCardImg;

    /**
     * 认证图片和视频
     */
    @NotBlank(message = "认证图片和视频不能为空")
    private String verifyImg;

    /**
     * 客户真是姓名
     */
    @NotBlank(message = "客户真是姓名不能为空")
    private String realName;

    /**
     * 客户身份证号
     */
    @NotBlank(message = "客户身份证号不能为空")
    private String idCard;

    /**
     * 客户手机号
     */
    @NotBlank(message = "客户手机号不能为空")
    private String phone;
}

FirmCustomerParam 实体参数类

@Data
public class FirmCustomerParam implements Serializable {

    /**
     * 企业营业执照
     */
    @NotBlank(message = "企业营业执照不能为空")
    private String businessLicense;

    /**
     * 企业名称
     */
    @NotBlank(message = "企业名称不能为空")
    private String firmName;


    /**
     * 企业信用代码
     */
    @NotBlank(message = "企业信用代码不能为空")
    private String creditCode;

    /**
     * 企业地址
     */
    @NotBlank(message = "企业地址不能为空")
    private String address;

    /**
     * 企业联系人
     */
    @NotBlank(message = "企业联系人不能为空")
    private String linkman;

    /**
     * 企业联系人电话
     */
    @NotBlank(message = "企业联系人电话不能为空")
    private String mobile;

}

(2)自定义参数校验注解 @CustomerParamTwo

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = CustomerParamValidatorTwo.class)//注解校验类
public @interface CustomerParamTwo {

    String message() default "Invalid customer parameters";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}

CustomerParamValidatorTwo 实现 ConstraintValidator接口 重写具体校验规则

import com.personal.validation.annotations.CustomerParamTwo;
import com.personal.validation.param.AddCustomerParam;
import com.personal.validation.param.FirmCustomerParam;
import com.personal.validation.param.SingleCustomerParam;
import com.personal.validation.utils.ValidatorUtils;
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
import jakarta.validation.constraints.NotBlank;

import java.lang.reflect.Field;

public class CustomerParamValidatorTwo implements ConstraintValidator<CustomerParamTwo, AddCustomerParam> {


    @Override
    public void initialize(CustomerParamTwo constraintAnnotation) {
        ConstraintValidator.super.initialize(constraintAnnotation);
    }

    @Override
    public boolean isValid(AddCustomerParam customerParam, ConstraintValidatorContext constraintValidatorContext) {

        String authType =customerParam.getAuthType();

        if ("1".equals(authType)) {
            if(customerParam.getSingleCustomerParam() instanceof SingleCustomerParam){
                return customerParam.getSingleCustomerParam() != null
                        && isValidSingleCustomerParam(customerParam.getSingleCustomerParam(), constraintValidatorContext);
            }else
                //当authType = 1 时,singleCustomerParam不能为空
                addConstraintViolation(constraintValidatorContext, "singleCustomerParam","个人客户信息不能为空");
                return false;
        }else if ("2".equals(authType)) {
            if(customerParam.getFirmCustomerParam() instanceof FirmCustomerParam){
                return customerParam.getFirmCustomerParam() != null
                        && isValidFirmCustomerParam(customerParam.getFirmCustomerParam(), constraintValidatorContext);
            }else
                //当authType = 2 时,firmCustomerParam不能为空
                addConstraintViolation(constraintValidatorContext, "firmCustomerParam","企业客户信息不能为空");
                return false;
        }
        //单独校验authType类型
        if(!("1".equals(authType) || "2".equals(authType) || "".equals(authType))){
            addConstraintViolation(constraintValidatorContext, "authType","认证类型不正确");
            return false;
        }
        return false;
    }
    
    private boolean isValidSingleCustomerParam(SingleCustomerParam param, ConstraintValidatorContext context) {

        boolean isValid = true ;
        
        Field[] fields = SingleCustomerParam.class.getDeclaredFields();//反射获取对象的所有字段
        for (Field field : fields) {
            if (field.isAnnotationPresent(NotBlank.class)) {//获取属性上是否标注了校验注解
                // 如果字段有 @NotBlank 注解,则进行非空验证
                if (!validateField(param, field, context)) {
                    isValid = false;
                }
            }
        }
        if(!ValidatorUtils.isValidPhoneNumber(param.getPhone())){
            addConstraintViolation(context, "phone","手机号不正确");
            isValid = false;
        }
        return isValid;
    }

    private boolean isValidFirmCustomerParam(FirmCustomerParam param, ConstraintValidatorContext context) {
        boolean isValid = true ;
        Field[] fields = FirmCustomerParam.class.getDeclaredFields();
        for (Field field : fields) {
            if (field.isAnnotationPresent(NotBlank.class)) {
                // 如果字段有 @NotBlank 注解,则进行非空验证
                if (!validateField(param, field, context)) {
                    isValid = false;
                }
            }
        }

        return isValid;
    }
    
    // 字段校验不通过后,获取校验错误信息
    private boolean validateField(Object param, Field field, ConstraintValidatorContext context) {
        try {
            field.setAccessible(true);
            Object value = field.get(param);
            if (value == null || value.toString().trim().isEmpty()) {
                String message = field.getAnnotation(NotBlank.class).message();
                addConstraintViolation(context, field.getName(),message);
                return false;
            }
            return true;
        } catch (IllegalAccessException e) {
            return false;
        }
    }
    
    //将所有校验不通过的错误信息,封装返回给前端
    private void addConstraintViolation(ConstraintValidatorContext context, String field, String message) {
        context.disableDefaultConstraintViolation();
        context.buildConstraintViolationWithTemplate(message)
                .addPropertyNode(field)
                .addConstraintViolation();
    }
}

校验控制层接口类

@RestController
@RequestMapping("/customer")
public class CustomerController {

    /**
     * @param addCustomerParam
     * @return
     */
    @PostMapping("/add")
    public String addCustomer(@RequestBody @Validated AddCustomerParam addCustomerParam) {
        System.out.println(addCustomerParam);
        // 客户认证参数全部交给自定义注解校验,我们后续只关注业务代码,将 检验逻辑 与 业务代码逻辑 分离

        return "add customer success";
    }
}

在校验类加上自定义的注解 @CustomerParamTwo

@Data
@CustomerParamTwo
public class AddCustomerParam implements Serializable {

    /**
     *  客户类型 1个体工商户 2:企业客户
     */
    @NotBlank(message = "客户类型不能为空")
    private String authType;

    /**
     * 1个体工商户
     */
    private SingleCustomerParam singleCustomerParam;

    /**
     * 2企业客户
     */
    private FirmCustomerParam firmCustomerParam;
}

接下来我们进行测试:
测试authType 为空 或则 authType !=1 或则 2
在这里插入图片描述
在这里插入图片描述

authType = 1 校验 singleCustomerParam 个体工商户参数
在这里插入图片描述
校验成功:
在这里插入图片描述

authType = 2 校验 firmCustomerParam 企业客户参数
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

四、validation 校验失败处理

从上述案例中,校验失败错误信息原本抛出的异常不是如此的,validation 校验异常需要自己手动拦截并封装错误信息返回。接下来将如何捕获 validation 的异常信息,并封装成一个对象返回给前端
(1)自定义封装结果类

public class R {
    private int code;
    private String message;
    private Object data;

    public R() {
    }

    public R(int code, String message) {
        this.code = code;
        this.message = message;
    }

    public static R ok() {
        return new R(200, "Success");
    }

    public static R fail(int code, String message) {
        return new R(code, message);
    }

    // Getters and setters
    // You may want to add additional methods for data handling
    public int getCode() {
        return code;
    }

    public void setCode(int code) {
        this.code = code;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public Object getData() {
        return data;
    }

    public void setData(Object data) {
        this.data = data;
    }
}

(2)自定义ValidationException 异常

public class ValidationException extends RuntimeException {
    public ValidationException(String message) {
        super(message);
    }
}

(3)自定义拦截器
直接在官网复制 validation 拦截器

@ControllerAdvice
public class CustomGlobalExceptionHandler {

    // Handle ConstraintViolationException
    @ExceptionHandler(ConstraintViolationException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ResponseBody
    public R handleValidationException(ConstraintViolationException ex) {
        List<String> errors = new ArrayList<>();
        for (ConstraintViolation<?> violation : ex.getConstraintViolations()) {
            errors.add(violation.getPropertyPath() + ": " + violation.getMessage());
        }
        return R.fail(HttpStatus.BAD_REQUEST.value(), errors.toString());
    }

    // Handle MethodArgumentTypeMismatchException
    @ExceptionHandler(MethodArgumentTypeMismatchException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ResponseBody
    public R handleMethodArgumentTypeMismatchException(MethodArgumentTypeMismatchException ex) {
        String error = ex.getName() + " should be of type " + ex.getRequiredType().getName();
        return R.fail(HttpStatus.BAD_REQUEST.value(), error);
    }

    // Handle BindException
    @ExceptionHandler(BindException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ResponseBody
    public R handleBindException(BindException ex) {
        List<String> errors = new ArrayList<>();
        for (FieldError error : ex.getFieldErrors()) {
            errors.add(error.getField() + ": " + error.getDefaultMessage());
        }
        return R.fail(HttpStatus.BAD_REQUEST.value(), errors.toString());
    }

    // Handle custom ValidationException
    @ExceptionHandler(ValidationException.class)  // 在这里进行拦截的
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ResponseBody
    public R handleValidationException(ValidationException ex) {
        return R.fail(HttpStatus.BAD_REQUEST.value(), ex.getMessage()); //拦截运行错误时的状态吗以及错误信息
    }

    // Handle generic exceptions
    @ExceptionHandler(Exception.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ResponseBody
    public R handleException(Exception ex) {
        return R.fail(HttpStatus.INTERNAL_SERVER_ERROR.value(), ex.getMessage());
    }
}

总结

例如:以上就是关于日常开发过程参数校验及封装错误信息返回给前端

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

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

相关文章

Webhook应用指南:借助mc工具实现智能自动化

欢迎来到我的博客&#xff0c;代码的世界里&#xff0c;每一行都是一个故事 &#x1f38f;&#xff1a;你只管努力&#xff0c;剩下的交给时间 &#x1f3e0; &#xff1a;小破站 Webhook应用指南&#xff1a;借助mc工具实现智能自动化 前言Webhook 基础知识什么是 Webhook&…

【全网最新】若依管理系统基于SpringBoot的前后端分离版本配置部署

目录 提前准备&#xff1a; 下载源代码 设置依赖 设置后台连接信息 运行后台 运行前端 安装npm依赖 启动前端 登录网页客户端 提前准备&#xff1a; 1、安装mysql 5以上就可以。 2、安装redis. 3、安装npm npm下载地址&#xff1a;https://nodejs.org/dist/v22.12…

科学演进:现代视角下的亚里士多德理论100例

科学演进&#xff1a;现代视角下的亚里士多德理论100例 引言 亚里士多德&#xff08;Aristotle&#xff09;&#xff0c;公元前384年出生于希腊北部的斯塔吉拉&#xff0c;是古希腊最杰出的哲学家、科学家和教育家之一。他不仅是柏拉图的学生&#xff0c;深入学习了柏拉图的理…

98.【C语言】数据结构之队列

目录 1.定义 2.队头和队尾 3.示意图 4.实现队列 两种解决方法 1.使用双向带头循环链表 2.为单向链表再定义一个尾指针tail 操作队列的函数 初始化函数QueueInit 插入函数QueuePush 删除函数QueuePop 写法1 注意 写法2 计算队列大小函数QueueSize 销毁函数Queu…

SQL项目实战与综合应用——项目设计与需求分析

项目设计与需求分析是软件开发过程中的核心环节&#xff0c;尤其在涉及数据库的应用时&#xff0c;良好的设计将直接影响到项目的可扩展性、性能和维护性。本文将深入探讨数据库设计的最佳实践&#xff0c;结合 C 与 SQL 的实际应用场景&#xff0c;涵盖项目需求收集、数据库设…

TCP的“可靠性”(下)——三次握手四次挥手

目录 建立连接&#xff08;三次握手&#xff09;为啥要进行握手&#xff1f;&#xff1f;意义何在&#xff1f;&#xff1f;常见面试题&#xff1a;为啥必须是三次握手&#xff1f; 断开连接&#xff08;四次挥手&#xff09;三次握手和四次挥手的相同点和不同点连接过程中涉及…

煤矿 35kV 变电站 3 套巡检机器人 “上岗”,力破供电瓶颈

近日&#xff0c;杭州旗晟智能科技与甘肃陈家沟煤矿35kV变电站配电室的三套智能巡检机器人线下测试顺利完成&#xff0c;并成功交付使用&#xff0c;这为陈家沟煤矿的电力运维工作注入了全新的活力与强大的技术支撑。 一、项目背景 甘肃陈家沟煤矿35kV变电站于2024年3月13日动…

【AI系统】推理参数

推理参数 本文将介绍 AI 模型网络参数方面的一些基本概念&#xff0c;以及硬件相关的性能指标&#xff0c;为后面让大家更了解模型轻量化做初步准备。值得让人思考的是&#xff0c;随着深度学习的发展&#xff0c;神经网络被广泛应用于各种领域&#xff0c;模型性能的提高同时…

列表| 习题+思维导图

列表习题 1. 已知列表xlist(range(9))&#xff0c;那么执行语句del x[:2]之后&#xff0c;x的值为&#xff08;D&#xff09; A.[1&#xff0c;3&#xff0c;5&#xff0c;7&#xff0c;9] B.[1&#xff0c;3&#xff0c;5&#xff0c;7] C.[0&#xff0c;1&#xff0c;3&a…

在freeBSD上开启IPSec内核功能

我使用的版本strongSwan 6.0.0, FreeBSD 14.1-RELEASE, amd64 背景 在freeBSD上配置strongSwan&#xff0c;启动charon时&#xff0c;一直遇到如下错误&#xff1a; unable to set IPSEC_POLICY on socket: Protocol not available过程 此前查阅的某个文档说 freeBSD已经默…

初识树(二叉树,堆,并查集)

本篇博客将涉及到一些专业名词&#xff1a;结点、深度、根、二叉树、满二叉树、完全二叉树、堆、并查集等。 概要 树型结构是一类重要的非线性数据结构。其中以树和二叉树最…

知从科技闪耀汽车智能底盘大会:共探软件安全新篇章

在汽车科技蓬勃发展的浪潮中&#xff0c;智能底盘技术正成为引领行业变革的关键力量。11月27日-28日&#xff0c;盖世汽车 2024 第四届汽车智能底盘大会盛大召开&#xff0c;上海知从科技有限公司受邀出席此次盛会&#xff0c;与众多汽车领域的精英齐聚一堂&#xff0c;共话智能…

TCP Analysis Flags 之 TCP Spurious Retransmission

前言 默认情况下&#xff0c;Wireshark 的 TCP 解析器会跟踪每个 TCP 会话的状态&#xff0c;并在检测到问题或潜在问题时提供额外的信息。在第一次打开捕获文件时&#xff0c;会对每个 TCP 数据包进行一次分析&#xff0c;数据包按照它们在数据包列表中出现的顺序进行处理。可…

241205_使用本地vosk模型实现流式语音识别

241205_使用本地vosk模型实现流式语音识别 上一篇我们使用了vosk模型实现了本地的语音识别&#xff0c;但是存在一个严重的问题&#xff0c;因为我们采用的是整段音频录制结束之后再进行转文字并进行关键字检索&#xff0c;就会导致识别不实时&#xff0c;在环境噪音较为复杂&…

CMake笔记之在CMakeLists.txt文件中开启Debug模式

CMake笔记之在CMakeLists.txt文件中开启Debug模式 code review! 文章目录 CMake笔记之在CMakeLists.txt文件中开启Debug模式1.设置 CMake 的构建类型2.添加编译器的调试选项3.使用 CMAKE_CXX_STANDARD (可选)4.编译和构建5.针对多配置生成器6.最终示例 CMakeLists.txt 1.设置 …

VINS_MONO视觉导航算法【一】基础知识介绍

文章目录 VINS-Mono其他文章说明简介单目相机存在的尺度不确定问题缺乏深度信息尺度等价性对极几何和三角化平移和深度的关系解决尺度不确定问题的方法视觉惯性里程计&#xff08;VIO&#xff09;初始尺度估计持续尺度校正 摄像头数据处理直接法&#xff08;Direct Method&…

【CC2530开发基础篇】光敏和热敏传感器

一、前言 1.1 开发背景 本实验通过CC2530单片机接入光敏传感器和热敏传感器&#xff0c;进行数据采集与检测&#xff0c;并将检测结果通过串口终端输出。光敏传感器和热敏传感器是常见的环境感知设备&#xff0c;分别用于测量光强和温度。在实际应用中&#xff0c;这些传感器…

计算机网络-GRE基础实验二

前面我们学习了GRE隧道的建立以及通过静态路由指向的方式使得双方能够网络互联&#xff0c;但是通过静态路由可能比较麻烦&#xff0c;GRE支持组播、单播、广播因此可以在GRE隧道中运行动态路由协议使得网络配置更加灵活。 通过前面的动态路由协议的学习我们知道动态路由协议都…

中建海龙:科技创新引领建筑业革新,铸就行业影响力

在建筑业这个古老而又充满活力的行业中&#xff0c;中建海龙科技有限公司&#xff08;以下简称“中建海龙”&#xff09;凭借其卓越的科技实力和一系列荣誉奖项&#xff0c;正逐步确立其在建筑工业化领域的领导地位&#xff0c;并对整个行业产生了深远影响。 中建海龙自成立以来…

Y20030028 JAVA+SSM+MYSQL+LW+基于JAVA的考研监督互助系统的设计与实现 源代码 配置 文档

基于JAVA的考研监督互助系统 1.项目描述2. 课题开发背景及意义3.项目功能4.界面展示5.源码获取 1.项目描述 随着高等教育的普及和就业竞争的加剧&#xff0c;越来越多的学生选择继续深造&#xff0c;参加研究生入学考试。考研人数的不断增加&#xff0c;使得考研过程中的学习监…