【译】Spring 6 入参数据校验: 综合指南

news2024/10/6 14:30:07

一、前言

在 Spring 6.1 中,有一个非常值得注意的重要改进——编程式验证器实现。Spring 长期以来一直通过注解支持声明式验证,而 Spring 6.1 则通过提供专用的编程式验证方法引入了这一强大的增强功能。

编程式验证允许开发人员对验证过程进行细粒度控制,实现动态和有条件的验证场景,超越了声明式方法的能力。在本教程中,我们将深入探讨实现编程式验证并将其与 Spring MVC 控制器无缝集成的细节。

二、声明式验证与编程式验证的区别

对于数据验证,Spring 框架有两种主要方法:声明式验证和编程式验证。

"声明式验证(Declarative validation)"通过域对象上的元数据或注解指定验证规则。Spring 利用 JavaBean Validation (JSR 380) 注释(如 @NotNull、@Size 和 @Pattern)在类定义中直接声明验证约束。

Spring 会在数据绑定过程中自动触发验证(例如,在 Spring MVC 表单提交过程中)。开发人员无需在代码中明确调用验证逻辑。

public class User {

  @NotNull
  private String username;
  
  @Size(min = 6, max = 20)
  private String password;
  // ...
}

另一方面,“编程式验证(Programmatic validation)” 在代码中编写自定义验证逻辑,通常使用 Spring 提供的 Validator 接口。这种方法可以实现更动态、更复杂的验证场景。

开发人员负责显式调用验证逻辑,通常在服务层或控制器中进行。

public class UserValidator implements Validator {

  @Override
  public boolean supports(Class<?> clazz) {
    return User.class.isAssignableFrom(clazz);
  }

  @Override
  public void validate(Object target, Errors errors) {
    User user = (User) target;
    // 自定义验证逻辑, 可以读取多个字段进行混合校验,编程的方式灵活性大大增加
  }
}

三、何时使用程序化验证

在声明式验证和编程式验证之间做出选择取决于用例的具体要求。

声明式验证通常适用于比较简单的场景,验证规则可以通过注释清晰地表达出来。声明式验证很方便,也符合惯例,即重于配置的原则。

程序化验证提供了更大的灵活性和控制力,适用于超出声明式表达范围的复杂验证场景。当验证逻辑取决于动态条件或涉及多个字段之间的交互时,程序化验证尤其有用。

我们可以将这两种方法结合起来使用。我们可以利用声明式验证的简洁性来处理常见的情况,而在面对更复杂的要求时,则采用编程式验证。

四、程序化验证器 API 简介

Spring 中的编程式验证器 API 的核心是允许创建自定义验证器类,并定义仅靠注解可能无法轻松捕获的验证规则。

以下是创建自定义验证器对象的一般步骤。

  • 创建一个实现 org.springframework.validation.Validator 接口的类。
  • 重载 supports() 方法,以指定该验证器支持哪些类。
  • 实现 validate()validateObject() 方法,以定义实际的验证逻辑。
  • 使用 ValidationUtils.rejectIfEmpty()ValidationUtils.rejectIfEmptyOrWhitespace() 方法,以给定的错误代码拒绝给定字段。
  • 我们可以直接调用 Errors.rejectValue() 方法来添加其他类型的错误。
import org.springframework.validation.Errors;
import org.springframework.validation.ValidationUtils;
import org.springframework.validation.Validator;

@Component
public class UserValidator implements Validator {

  @Override
  public boolean supports(Class<?> clazz) {
      return User.class.isAssignableFrom(clazz);
  }

  @Override
  public void validate(Object target, Errors errors) {

    User user = (User) target;

    // 例如: 校验 username 不能为空
    ValidationUtils.rejectIfEmptyOrWhitespace(errors, 
      "username", "field.required", "Username must not be empty.");

    // 添加更多的自定义验证逻辑
  }
}

要使用自定义验证器,我们可以将其注入 @Controller 或 @Service 等 Spring 组件,或者直接将其实例化。然后,我们调用验证方法,传递要验证的对象和 Errors 对象以收集验证错误。

public class UserService {

  private Validator userValidator;

  public UserService(Validator userValidator) {
    this.userValidator = userValidator;
  }

  public void someServiceMethod(User user) {

    Errors errors = new BeanPropertyBindingResult(user, "user");
    userValidator.validate(user, errors);

    if (errors.hasErrors()) {
      // 处理数据校验错误
    }
  }
}

五、初始化安装

5.1. Maven 配置

要使用编程式验证器,我们需要 Spring Framework 6.1 或 Spring Boot 3.2,因为这些是最低支持的版本。

<parent>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-parent</artifactId>
  <version>3.2.0</version>
  <relativePath/>
</parent>

5.2. 领域对象

本教程的领域对象是雇员(Employee) 和部门(Department)对象。我们不会创建复杂的结构,因此可以专注于核心概念。

Employee.java

package demo.customValidator.model;

@Data
@Builder
public class Employee {

  Long id;
  String firstName;
  String lastName;
  String email;
  boolean active;

  Department department;
}

Department.java

package demo.customValidator.model;

@Data
@Builder
public class Department {

  Long id;
  String name;
  boolean active;
}

六、 实现程序化验证器

以下 EmployeeValidator 类实现了 org.springframework.validation.Validator 接口并实现了必要的方法。它将根据需要在 Employee 字段中添加验证规则。

package demo.customValidator.validator;

import demo.customValidator.model.Employee;
import org.springframework.validation.Errors;
import org.springframework.validation.ValidationUtils;
import org.springframework.validation.Validator;

public class EmployeeValidator implements Validator {

  @Override
  public boolean supports(Class<?> clazz) {
    return Employee.class.isAssignableFrom(clazz);
  }

  @Override
  public void validate(Object target, Errors errors) {

    ValidationUtils.rejectIfEmpty(errors, "id", ValidationErrorCodes.ERROR_CODE_EMPTY);
    ValidationUtils.rejectIfEmptyOrWhitespace(errors, "firstName", "First name cannot be empty");
    ValidationUtils.rejectIfEmptyOrWhitespace(errors, "lastName", "Last name cannot be empty");
    ValidationUtils.rejectIfEmptyOrWhitespace(errors, "email", "Email cannot be empty");

    Employee employee = (Employee) target;

    if (employee.getFirstName() != null && employee.getFirstName().length() < 3) {
      errors.rejectValue("firstName", "First name must be greater than 2 characters");
    }

    if (employee.getLastName() != null && employee.getLastName().length() < 3) {
      errors.rejectValue("lastName", "Last name must be greater than 3 characters");
    }
  }
}

同样,我们为 Department 类定义了验证器。如有必要,您可以添加更复杂的验证规则。

package demo.customValidator.model.validation;

import demo.customValidator.model.Department;
import org.springframework.validation.Errors;
import org.springframework.validation.ValidationUtils;
import org.springframework.validation.Validator;

public class DepartmentValidator implements Validator {

  @Override
  public boolean supports(Class<?> clazz) {
    return Department.class.equals(clazz);
  }

  @Override
  public void validate(Object target, Errors errors) {

    ValidationUtils.rejectIfEmpty(errors, "id", ValidationErrorCodes.ERROR_CODE_EMPTY);
    ValidationUtils.rejectIfEmptyOrWhitespace(errors, "name", "Department name cannot be empty");

    Department department = (Department) target;

    if(department.getName() != null && department.getName().length() < 3) {
      errors.rejectValue("name", "Department name must be greater than 3 characters");
    }
  }
}

现在我们可以验证 EmployeeDepartment 对象的实例,如下所示:

Employee employee = Employee.builder().id(2L).build();
//Aurowire if needed
EmployeeValidator employeeValidator = new EmployeeValidator();

Errors errors = new BeanPropertyBindingResult(employee, "employee");
employeeValidator.validate(employee, errors);

if (!errors.hasErrors()) {
  System.out.println("Object is valid");
} else {
  for (FieldError error : errors.getFieldErrors()) {
    System.out.println(error.getCode());
  }
}

程序输出:

First name cannot be empty
Last name cannot be empty
Email cannot be empty

Department 对象也可以进行类似的验证。

七、链式多个验证器

在上述自定义验证器中,如果我们验证了雇员对象,那么 API 将不会验证部门对象。理想情况下,在验证特定对象时,应针对所有关联对象执行验证。

程序化验证 API 允许调用其他验证器,汇总所有错误,最后返回结果。使用 ValidationUtils.invokeValidator() 方法可以实现这一功能,如下所示:

public class EmployeeValidator implements Validator {

  DepartmentValidator departmentValidator;

  public EmployeeValidator(DepartmentValidator departmentValidator) {
    if (departmentValidator == null) {
      throw new IllegalArgumentException("The supplied Validator is null.");
    }
    if (!departmentValidator.supports(Department.class)) {
      throw new IllegalArgumentException("The supplied Validator must support the Department instances.");
    }
    this.departmentValidator = departmentValidator;
  }

  @Override
  public void validate(Object target, Errors errors) {

    //...

    try {
      errors.pushNestedPath("department");
      ValidationUtils.invokeValidator(this.departmentValidator, employee.getDepartment(), errors);
    } finally {
      errors.popNestedPath();
    }
  }
}
  • pushNestedPath() 方法允许为子对象设置临时嵌套路径。在上例中,当对部门对象进行验证时,路径被设置为 employee.department
  • 在调用 pushNestedPath() 方法之前,popNestedPath() 方法会将路径重置为原始路径。在上例中,它再次将路径重置为 employee

现在,当我们验证 Employee 对象时,也可以看到 Department 对象的验证错误。

Department department = Department.builder().id(1L).build();
Employee employee = Employee.builder().id(2L).department(department).build();

EmployeeValidator employeeValidator = new EmployeeValidator(new DepartmentValidator());

Errors errors = new BeanPropertyBindingResult(employee, "employee");
employeeValidator.validate(employee, errors);

if (!errors.hasErrors()) {
  System.out.println("Object is valid");
} else {
  for (FieldError error : errors.getFieldErrors()) {
    System.out.println(error.getField());
    System.out.println(error.getCode());
  }
}

程序输出:

firstName
First name cannot be empty

lastName
Last name cannot be empty

email
Email cannot be empty

department.name
Department name cannot be empty

注意打印出来的字段名称是 department.name。由于使用了 pushNestedPath() 方法,所以添加了 department. 前缀。

八、使用带消息解析功能的 MessageSource

使用硬编码的消息并不是一个好主意,因此我们可以将消息添加到资源文件(如 messages.properties)中,然后使用 MessageSource.getMessage() 将消息解析为所需的本地语言,从而进一步改进此代码。

例如,让我们在资源文件中添加以下消息:

error.field.empty={0} cannot be empty
error.field.size={0} must be between 3 and 20

为了统一访问,请在常量文件中添加以下代码。请注意,这些错误代码是在自定义验证器实现中添加的。

public class ValidationErrorCodes {
  public static String ERROR_CODE_EMPTY = "error.field.empty";
  public static String ERROR_CODE_SIZE = "error.field.size";
}

现在,当我们解析信息时,就会得到属性文件的信息。

MessageSource messageSource;

//...

if (!errors.hasErrors()) {
  System.out.println("Object is valid");
} else {
  for (FieldError error : errors.getFieldErrors()) {

    System.out.println(error.getCode());
    System.out.println(messageSource.getMessage(
      error.getCode(), new Object[]{error.getField()}, Locale.ENGLISH));
  }
}

程序输出:

error.field.empty
firstName cannot be empty

error.field.empty
lastName cannot be empty

error.field.empty
email cannot be empty

error.field.empty
department.name cannot be empty

九、将编程式验证器与 Spring MVC/WebFlux 控制器集成

将编程式验证器与 Spring MVC 控制器集成,包括将编程式验证器注入控制器、在 Spring 上下文中配置它们,以及利用 @Valid 和 BindingResult 等注解简化验证。

令人欣慰的是,这种集成还能解决 Ajax 表单提交和控制器单元测试问题。

下面是一个使用我们在前面章节中创建的 EmployeeValidator 对象的 Spring MVC 控制器的简化示例。

import demo.app.customValidator.model.Employee;
import demo.app.customValidator.model.validation.DepartmentValidator;
import demo.app.customValidator.model.validation.EmployeeValidator;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.*;

@Controller
@RequestMapping("/employees")
public class EmployeeController {

  @InitBinder
  protected void initBinder(WebDataBinder binder) {
    // 注入 编程式验证器
    binder.setValidator(new EmployeeValidator(new DepartmentValidator()));
  }

  @GetMapping("/registration")
  public String showRegistrationForm(Model model) {
    model.addAttribute("employee", Employee.builder().build());
    return "employee-registration-form";
  }

  @PostMapping("/processRegistration")
  public String processRegistration(
    @Validated @ModelAttribute("employee") Employee employee,
    BindingResult bindingResult) {

    if (bindingResult.hasErrors()) {
      return "employee-registration-form";
    }

    // 处理成功通过数据校验后表单的逻辑
    // 通常涉及数据库操作、身份验证等。

    return "employee-registration-confirmation"; // 重定向至成功页面
  }
}

之后,当表单提交时,可以使用 ${#fields.hasErrors('*')} 表达式在视图中显示验证错误。

在下面的示例中,我们在两个地方显示验证错误,即在表单顶部的列表中显示所有错误,然后显示单个字段的错误。请根据自己的要求定制代码。

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Employee Registration</title>
</head>
<body>

<h2>Employee Registration Form</h2>

<!-- Employee Registration Form -->
<form action="./processRegistration" method="post" th:object="${employee}">

    <!-- Display validation errors, if any -->
    <div th:if="${#fields.hasErrors('*')}">
        <div style="color: red;">
            <p th:each="error : ${#fields.errors('*')}" th:text="${error}"></p>
        </div>
    </div>

    <!-- Employee ID (assuming it's a hidden field for registration) -->
    <input type="hidden" th:field="*{id}" />

    <!-- Employee First Name -->
    <label for="firstName">First Name:</label>
    <input type="text" id="firstName" th:field="*{firstName}" required />
    <span th:if="${#fields.hasErrors('firstName')}" th:text="#{error.field.size}"></span>
    <br/>

    <!-- Employee Last Name -->
    <label for="lastName">Last Name:</label>
    <input type="text" id="lastName" th:field="*{lastName}" required />
    <span th:if="${#fields.hasErrors('lastName')}" th:text="#{error.field.size}"></span>
    <br/>

    <!-- Employee Email -->
    <label for="email">Email:</label>
    <input type="email" id="email" th:field="*{email}" required />
    <span th:if="${#fields.hasErrors('email')}" th:text="#{error.field.size}"></span>
    <br/>

    <!-- Employee Active Status -->
    <label for="active">Active:</label>
    <input type="checkbox" id="active" th:field="*{active}" />
    <br/>

    <!-- Department Information -->
    <h3>Department:</h3>
    <label for="department.name">Department Name:</label>
    <input type="text" id="department.name" th:field="*{department.name}" required />
    <span th:if="${#fields.hasErrors('department.name')}" th:text="#{error.field.size}"></span>

    <br/>

    <!-- Submit Button -->
    <button type="submit">Register</button>

</form>

</body>
</html>

当我们运行应用程序并提交无效表单时,会出现如图所示的错误:

在这里插入图片描述

十、单元测试编程式验证器

我们可以将自定义验证器作为模拟依赖关系或单个测试对象进行测试。下面的 JUnit 测试用例将测试 EmployeeValidator

我们编写了两个非常简单的基本测试供快速参考,您也可以根据自己的需求编写更多测试。

import demo.app.customValidator.model.Department;
import demo.app.customValidator.model.Employee;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.springframework.validation.BeanPropertyBindingResult;
import org.springframework.validation.Errors;

public class TestEmployeeValidator {

  static EmployeeValidator employeeValidator;

  @BeforeAll
  static void setup() {
    employeeValidator = new EmployeeValidator(new DepartmentValidator());
  }

  @Test
  void validate_ValidInput_NoErrors() {
    // Set up a valid user
    Employee employee = Employee.builder().id(1L)
      .firstName("Lokesh").lastName("Gupta").email("admin@howtodoinjava.com")
      .department(Department.builder().id(2L).name("Finance").build()).build();

    Errors errors = new BeanPropertyBindingResult(employee, "employee");
    employeeValidator.validate(employee, errors);

    Assertions.assertFalse(errors.hasErrors());
  }

  @Test
  void validate_InvalidInput_HasErrors() {
    // Set up a valid user
    Employee employee = Employee.builder().id(1L)
      .firstName("A").lastName("B").email("C")
      .department(Department.builder().id(2L).name("HR").build()).build();

    Errors errors = new BeanPropertyBindingResult(employee, "employee");
    employeeValidator.validate(employee, errors);

    Assertions.assertTrue(errors.hasErrors());
    Assertions.assertEquals(3, errors.getErrorCount());
  }
}

最佳做法是确保测试涵盖边缘情况和边界条件。这包括输入处于允许的最小值或最大值的情况。

十一、结论

在本教程中,我们通过示例探讨了 Spring 6.1 Programmatic Validator API 及其实施指南。程序化验证允许开发人员对验证过程进行细粒度控制。

我们讨论了如何创建和使用自定义验证器类,并将其与 Spring MVC 控制器集成。我们学习了如何使用消息解析,随后还讨论了如何测试这些验证器以实现更强大的编码实践。

代码地址:programmatic-validator

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【LeetCode:2824. 统计和小于目标的下标对数目 | 模拟+二分】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…

opencv-背景减除

背景减除&#xff08;Background Subtraction&#xff09;是一种用于从视频序列中提取前景对象的计算机视觉技术。该技术的主要思想是通过建模和维护场景的背景&#xff0c;从而检测出在不同时间点出现的前景对象。 OpenCV 提供了一些用于背景减除的函数&#xff0c;其中最常用…

2023算力行业深度报告:算力调度运营进程加速

今天分享的是算力系列深度研究报告&#xff1a;《2023算力行业深度报告&#xff1a;算力调度运营进程加速》。 &#xff08;报告出品方&#xff1a;东方证券&#xff09; 报告共计&#xff1a;17页 一、全国一体化算力网络建设逐步深化&#xff0c;算力有望成为普惠大众的基础…

京东数据分析:2023年10月京东彩妆销售大数据采集

鲸参谋监测的京东平台10月份彩妆市场销售数据已出炉&#xff01; 鲸参谋数据显示&#xff0c;今年10月份&#xff0c;京东平台上彩妆市场的销量将近430万&#xff0c;环比增长约21%&#xff0c;同比下滑约3%&#xff1b;销售额将近5.8亿&#xff0c;环比增长约7%&#xff0c;同…

11.7统一功能处理

一.登录拦截器 1.实现一个普通的类,实现HeadlerInterceptor接口,重写preHeadler方法. 2.将拦截器添加到配置中,并设定拦截规则. 二.访问前缀添加 方法1: 方法2:properties 三.统一异常处理 以上返回的是空指针异常,如果是别的异常就不会识别,建议加上最终异常 . 四.统一数据格…

电脑如何禁止截屏

禁止电脑截屏是一项重要的安全措施&#xff0c;可以保护用户隐私和防止恶意软件的使用。以下是几种禁止电脑截屏的方法&#xff1a; 形式一&#xff1a; 一刀切&#xff0c;全部禁止截屏 可以在域之盾软件后&#xff0c;点击桌面管理&#xff0c;然后选择禁止截屏。就能禁止所…

分布式篇---第四篇

系列文章目录 文章目录 系列文章目录前言一、分布式ID生成有几种方案?二、幂等解决方法有哪些?三、常见负载均衡算法有哪些?前言 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站,这篇文章男女通用,看懂了就去分享给…

【解决】HDFS JournalNode启动慢问题排查

文章目录 一. 问题描述二. 问题分析1. 排查机器性能2. DNS的问题 三. 问题解决 一句话&#xff1a;因为dns的问题导致journalnode启动时很慢&#xff0c;通过修复dns对0.0.0.0域名解析&#xff0c;修复此问题。 一. 问题描述 从journalnode启动到服务可用&#xff0c;完成RPC…

Openwrt linux 启动流程

OpenWRT 启动流程 内核启动过程&#xff1a;【/init/mian.c】 Uboot --> start_kernel() --> rest_init() --> kernel_thread(kernel_init) --> kernel_init_freeable() 初始化过程&#xff1a; Linux Kernel(kernel_init) --> /etc/preinit --> /sbin/in…

Debian12试用报告

环境: win11vbox 虚拟机 网络: host-only访问局域网 nat 访问外网, 配置为dhcp动态获取ip 遇到的问题: 偶尔卡死: nat每次开机都不生效, 外网无法访问; 开机后 重启网络可解决 sudo /etc/init.d/networking restart host-only倒是没问题, 内网正常访问 vim9还是用不习…

2016年8月18日 Go生态洞察:Go 1.7版本二进制文件缩小

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f984; 博客首页——&#x1f405;&#x1f43e;猫头虎的博客&#x1f390; &#x1f433; 《面试题大全专栏》 &#x1f995; 文章图文…

Lubuntu 23.10用户可使用LXQt 1.4桌面

导读在众多 Lubuntu 用户的要求下&#xff0c;Lubuntu 开发人员决定将 LXQt 1.4 桌面环境向后移植到最新的 Lubuntu 23.10 &#xff08;Mantic Minotaur&#xff09; 版本。 是的&#xff0c;您没看错&#xff0c;您现在可以使用官方的 Lubuntu Backports PPA&#xff08;个人软…

Ubuntu 22.04安装vscode

要在Ubuntu 22.04安装vscode&#xff0c;请完成这些步骤。 首先apt命令更新软件包索引并安装导入微软GPG密钥的依赖软件。 更新&#xff0c;近期内执行过可忽略 sudo apt update安装工具包 sudo apt install software-properties-common apt-transport-https curl当导入GPG后…

MacOS “xxxxx“,已损坏,无法打开,你应该将它移到废纸篓

在这里插入图片描述 解决方案 应用程序 - 实用工具中打开终端&#xff0c;输入命令&#xff0c; sudo xattr -r -d com.apple.quarantine 然后将程序拖放至命令窗口&#xff0c;如下图&#xff1a;

「Whale 帷幄」连续入选科技榜单,AGI 冲击波正在加速行业洗牌

以 AGI 为底座&#xff0c;品牌 MarTech 正在经历一场前所未有的深度变革。 近日&#xff0c;弯弓研究院发布「中国 MarTech 500 强榜单」&#xff0c;以 2023 中国营销技术&#xff08;MarTech&#xff09;生态为研究对象&#xff0c;洞察行业现象与未来趋势。作为品牌数字化…

Wireshark的捕获过滤器

Wireshark的过滤器&#xff0c;顾名思义&#xff0c;作用是对数据包进行过滤处理。具体过滤器包括捕获过滤器和显示过滤器。本文对捕获过滤器进行分析。 捕获过滤器&#xff1a;当进行数据包捕获时&#xff0c;只有那些满足给定的包含/排除表达式的数据包会被捕获。 捕获过滤器…

想基于AI变现吗,这个Star有1.8K的开源项目分享给你

公众号「架构成长指南」&#xff0c;专注于生产实践、云原生、分布式系统、大数据技术分享。 前言 在如今AI爆发的时代&#xff0c;每个人都想借着AI这股风&#xff0c;进行变现&#xff0c;今天给大家分享一个开源项目&#xff0c;他可以让你基于AI的能力进行变现 项目介绍 …

百度文心一言(千帆大模型)聊天API使用指导

开篇不得不吐槽下百度&#xff0c;百度智能云平台首页跳转千帆大模型平台的按钮太多了&#xff0c;不同按钮跳转不同的子页面&#xff0c;不熟悉的&#xff0c;能把人找懵。入口太多&#xff0c;就导致用户不知道从何开始。本文就从一个前端开发人员的角度&#xff0c;教大家快…

小程序:project.config.json / project.private.config.json / 项目配置文件 /拉取代码产生冲突 / 如何解决

一、理解project.config.json / project.private.config.json project.config.json 文件是项目的配置文件&#xff0c;它包含了关于小程序的一些基本信息&#xff0c;例如小程序的名称、App ID、开发者信息以及页面路径等。这个文件一般不会被提交到版本控制系统中&#xff0c;…

面试题:工作中做过 JVM 调优吗?怎么做的?

文章目录 前言cpu占用过高死锁内存泄漏上面只是其中一种处理方法 总结 前言 最近很多小伙伴跟我说&#xff0c;自己学了不少JVM的调优知识&#xff0c;但是在实际工作中却不知道何时对JVM进行调优。今天&#xff0c;我就为大家介绍几种JVM调优的场景。 在阅读本文时&#xff…