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

Spring Boot:现代化Java应用开发的艺术

目录

什么是Spring Boot?

为什么选择Spring Boot?

Spring Boot的核心概念

详细步骤:创建一个Spring Boot应用

步骤1:使用Spring Initializr创建项目

步骤2:解压并导入项目

步骤3:构建和配置项目

pom.xml(Maven项目)

application.yml(配置文件)

步骤4:创建实体和仓库

User.java(实体类)

步骤5:创建映射方法

   Mapper,BaseMapper

步骤6:创建服务类

UserService.java(服务类)

步骤7:创建映射文件

    EmpDaoMapperInfo.xml

步骤8:创建控制类

   Controller

步骤9:运行应用

步骤10:测试应用

总结


1.什么是Spring Boot?

Spring Boot是Pivotal团队(现为VMware的一部分)提供的一套快速开发Spring应用的脚手架。它基于Spring框架,通过提供一系列的“Starters”和自动配置,简化了Spring应用的初始搭建和开发过程。Spring Boot的目标是让你尽可能快地运行和部署Spring应用。

2.为什么选择Spring Boot?

  1. 快速开发:Spring Boot提供了快速构建和部署Spring应用的能力。
  2. 开箱即用:内嵌了Tomcat、Jetty等服务器,无需部署WAR文件。
  3. 自动配置:根据添加的依赖自动配置Spring应用,减少手动配置。
  4. 无代码生成:不需要生成额外的XML或Java配置代码。
  5. 生产就绪:提供了监控和管理应用的工具,如指标、健康检查等。

3.Spring Boot的核心概念

  1. Spring Boot Starter:提供项目所需的依赖集合,简化依赖管理。
  2. Spring Boot Auto-Configuration:自动配置Spring和第三方库,无需手动配置。
  3. Spring Boot Actuator:提供应用监控和管理的端点。
  4. Spring Boot CLI:命令行工具,用于快速开发和测试Spring应用。
  5. Spring Initializr:在线工具,用于快速生成Spring Boot项目结构。

4.详细步骤:创建一个Spring Boot应用

步骤1:使用Spring Initializr创建项目

访问Spring Initializr,选择以下选项:

  • Project: Maven Project 或 Gradle Project
  • Language: Java
  • Spring Boot版本:选择最新的稳定版本
  • Group: com.example
  • Artifact: demo
  • Dependencies: Spring Web, Spring Data JPA, H2 Database

点击“Generate”按钮,下载生成的项目压缩包。

步骤2:解压并导入项目

将下载的压缩包解压,并使用IDE(如IntelliJ IDEA或Eclipse)导入项目。

步骤3:构建和配置项目

pom.xml(Maven项目)

确保pom.xml文件中包含了以下依赖:

<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!--spring-mybatis整合包--><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>2.1.3</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.33</version></dependency><dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>1.1.10</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><scope>runtime</scope><optional>true</optional></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-configuration-processor</artifactId><optional>true</optional></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies>
application.yml(配置文件)

src/main/resources目录下,编辑application.properties文件,添加数据库配置:

#DB Configuration:
spring:#连接池配置datasource:type: com.alibaba.druid.pool.DruidDataSourcedruid:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/schoolbase?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghaiusername: rootpassword: 123456initial-size: 1                     #连接池初始大小max-active: 20                      #连接池中最大的活跃连接数min-idle: 1                         #连接池中最小的活跃连接数max-wait: 60000                     #配置获取连接等待超时的时间pool-prepared-statements: true    #打开PSCache,并且指定每个连接上PSCache的大小max-pool-prepared-statement-per-connection-size: 20validation-query: SELECT 1 FROM DUALvalidation-query-timeout: 30000test-on-borrow: false             #是否在获得连接后检测其可用性test-on-return: false             #是否在连接放回连接池后检测其可用性test-while-idle: true             #是否在连接空闲一段时间后检测其可用性#mybatis配置
mybatis:mapper-locations: classpath:mapper/*MapperInfo.xmltype-aliases-package: com.fs.pojoconfiguration:log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

步骤4:创建实体和仓库

User.java(实体类)
package com.fs.pojo;import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import org.springframework.format.annotation.DateTimeFormat;import java.util.Date;@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class Emp {private Integer empno;private String ename;private String pass;private String job;private Integer deptno;private Integer mgr;@DateTimeFormat(pattern = "yyyy-MM-dd") //提交请求的日期转换@JsonFormat(pattern = "yyyy-MM-dd",timezone = "GMT+8") //响应json转换private Date hiredate;private Double sal;
}

步骤5:创建映射方法

   Mapper,BaseMapper

这是基础映射接口:

package com.fs.mapper;import java.io.Serializable;
import java.util.List;//基础映射接口,公共的方法public interface BaseMapper<T> {/*** 插入一条记录* @param entity* 实体对象* @return int*/Integer insert(T entity) throws  Exception;/*** 根据 ID 修改* @param entity* 实体对象* @return int*/Integer update(T entity)throws  Exception;/*** 根据 ID 删除* @param id* 主键ID* @return int*/Integer deleteById(Serializable id) throws  Exception;/*** 根据 ID 查询* @param id* 主键ID* @return T*/T selectById(Serializable id) throws  Exception;/*** 查询所有* @return List<T>*/List<T> selectAll() throws  Exception;}

这是自己的Mapper直接继承BaseMapper,再写一些额外的方法

package com.fs.mapper;import com.fs.pojo.Emp;
import com.fs.vo.Condition;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;import java.util.List;@Repository
@Mapper  //接口实现映射代理
public interface EmployeeMapper extends BaseMapper<Emp>{/*** 登录功能* @param employeeModel 密码 用户名* @return 用户对象*/Emp login(Emp employeeModel);/*** 根据条件查询* @param condition 条件对象* @return 结果集合*/List<Emp> selectByCondition(Condition condition);}

步骤6:创建服务类

UserService.java(服务类)

服务类接口:

package com.fs.service;import com.fs.pojo.Emp;
import com.fs.vo.Condition;import java.util.List;public interface EmpService {int addEmp(Emp employeeModel) throws Exception;//int updateEmp(EmployeeModel employeeModel) throws Exception;int delete(int empno) throws Exception;List<Emp> queryEmps() throws Exception;Emp login(Emp employeeModel);//条件查询List<Emp> queryByCondition(Condition condition);
}

实现类:

package com.fs.service.impl;import com.fs.mapper.EmployeeMapper;
import com.fs.pojo.Emp;
import com.fs.service.EmpService;
import com.fs.vo.Condition;
import org.springframework.stereotype.Service;import javax.annotation.Resource;
import java.util.List;@Service
public class EmpServiceImpl implements EmpService {//注入映射代理@Resourceprivate EmployeeMapper employeeMapper;@Overridepublic int addEmp(Emp employeeModel) throws Exception {return employeeMapper.insert(employeeModel);}@Overridepublic int delete(int empno) throws Exception {return employeeMapper.deleteById(empno);}@Overridepublic List<Emp> queryEmps() throws Exception {return employeeMapper.selectAll();}@Overridepublic Emp login(Emp employeeModel) {return employeeMapper.login(employeeModel);}@Overridepublic List<Emp> queryByCondition(Condition condition) {return employeeMapper.selectByCondition(condition);}
}

步骤7:创建映射文件

    EmpDaoMapperInfo.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.fs.mapper.EmployeeMapper"><select id="selectAll" resultMap="empmap">select * from emp</select><select id="login" parameterType="Emp" resultMap="empmap">select * from emp whereename=#{ename} and pass=#{pass}</select><delete id="deleteById" parameterType="int">delete from emp where empno = #{empno}</delete><select id="selectById" parameterType="java.lang.Integer" resultMap="empmap">select * from emp where empno=#{no}</select><insert id="insert" parameterType="Emp">insert into emp (empno,ename,job,sal,pass,hiredate)values (#{empno},#{ename},#{job},#{sal},#{pass},#{hiredate})</insert><select id="selectByCondition" parameterType="com.fs.vo.Condition"resultMap="empmap">select * from emp</select><resultMap id="empmap" type="Emp"><id column="empno" property="empno"></id><result column="ename" property="ename"></result><result column="job" property="job"></result><result column="sal" property="sal"></result><result column="pass" property="pass"></result><result column="hiredate" property="hiredate"></result></resultMap></mapper>

步骤8:创建控制类

   Controller
package com.fs.controller;import com.fs.pojo.Dept;
import com.fs.pojo.Emp;
import com.fs.service.DeptService;
import com.fs.service.EmpService;
import com.fs.vo.CommonResultVo;
import com.fs.vo.Condition;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import javax.annotation.Resource;
import java.util.HashMap;
import java.util.List;
import java.util.Map;@RestController
//@RequestMapping("/emp")
public class EmyController {@Resourceprivate EmpService empService;@Resourceprivate DeptService deptService;@RequestMapping("/test")public String empTest(){return "test";}@PostMapping("/add")public Map add(Emp employeeModel) throws Exception {int i = empService.addEmp(employeeModel);Map map = new HashMap();map.put("code",200);map.put("msg","插入成功");//转换成json对象return map;}@RequestMapping("/query")public Map queryAll() throws Exception {//查询所有数据List<Emp> employeeModels = empService.queryEmps();Map map = new HashMap();map.put("code",200);map.put("msg","查询成功");map.put("data",employeeModels);map.put("totals",employeeModels.size());return map;}@RequestMapping("/querybyconition")public CommonResultVo<Emp> queryByCondition(Condition condition){List<Emp> employeeModels = empService.queryByCondition(condition);//返回统一的公共响应类型对象CommonResultVo<Emp> commonResultVo = new CommonResultVo();commonResultVo.setCode(200);commonResultVo.setMsg("多条件查询成功");commonResultVo.setList(employeeModels);commonResultVo.setTotal(employeeModels.size());return commonResultVo;}//@RequestMapping("/login")@PostMapping("/login")public CommonResultVo login(Emp employeeModel){Emp employee = empService.login(employeeModel);CommonResultVo commonResultVo = new CommonResultVo();if (employee!= null) {commonResultVo.setCode(200);commonResultVo.setMsg("登录成功");commonResultVo.setData(employee);}else{commonResultVo.setCode(500);commonResultVo.setMsg("登录失败");}return commonResultVo;}@RequestMapping("/queryDept")public CommonResultVo getAllDept() throws Exception {List<Dept> depts = deptService.queryAll();CommonResultVo commonResultVo = new CommonResultVo();commonResultVo.setCode(200);commonResultVo.setMsg("部门查询成功");commonResultVo.setList(depts);return  commonResultVo;}}
温馨提示:当然你还可以写一些前端页面用于展示

步骤9:运行应用

运行主应用类DemoApplication,应用将启动并监听默认的8080端口。

步骤10:测试应用

使用Postman或任何HTTP客户端工具,发送请求到http://localhost:8080/users来测试你的应用。

  • GET /users:获取所有用户列表。
  • POST /users:创建一个新用户。

5.总结

Spring Boot通过提供一系列的Starters、自动配置和简化的部署流程,极大地提高了Java应用的开发效率。通过上述步骤,你可以快速搭建一个具有RESTful API的Spring Boot应用,并使用Spring Data JPA进行数据持久化。Spring Boot的这些特性使得它成为现代Java应用开发的不二选择。

相关文章:

Spring Boot:现代化Java应用开发的艺术

目录 什么是Spring Boot&#xff1f; 为什么选择Spring Boot&#xff1f; Spring Boot的核心概念 详细步骤&#xff1a;创建一个Spring Boot应用 步骤1&#xff1a;使用Spring Initializr创建项目 步骤2&#xff1a;解压并导入项目 步骤3&#xff1a;构建和配置项目 po…...

Redis五种基本数据结构的使用

Redis具有五种基本数据类型&#xff1a;String(字符串)、Hash(哈希)、List(列表)、Set(集合)、SortedSet(有序集合)&#xff0c;下面示意它们的使用。 String类数据类型的使用 增&#xff1a;添加数据(set)、添加多个数据(mset)、添加数据时指定过期时间(setex) ​ 删&#xf…...

【QT】系统-下

欢迎来到Cefler的博客&#x1f601; &#x1f54c;博客主页&#xff1a;折纸花满衣 &#x1f3e0;个人专栏&#xff1a;QT 目录 &#x1f449;&#x1f3fb;QTheadrun() &#x1f449;&#x1f3fb;QMutex&#x1f449;&#x1f3fb;QWaitCondition&#x1f449;&#x1f3fb;Q…...

java和kotlin 可以同时运行吗

Java 和 Kotlin 可以同时运行在同一个项目中&#xff0c;这主要得益于 Kotlin 对 Java 的互操作性。Kotlin 被设计为与 Java 100% 兼容&#xff0c;这意味着 Kotlin 代码可以很容易地调用 Java 代码&#xff0c;反之亦然。这种设计使得 Kotlin 能够无缝集成到现有的 Java 项目中…...

2024最新版 Tuxera NTFS for Mac 2023绿色版图文安装教程

​ 在数字化时代&#xff0c;数据的存储和传输变得至关重要。Mac用户经常需要在Windows NTFS格式的移动硬盘上进行读写操作&#xff0c;然而&#xff0c;由于MacOS系统默认不支持NTFS的写操作&#xff0c;这就需要我们寻找一款高效的读写软件。Tuxera NTFS for Mac 2023便是其中…...

npm发布插件超级简单版

在开源的世界里&#xff0c;每个人都有机会成为贡献者&#xff0c;甚至是创新的引领者。您是否有过这样的想法&#xff1a;开发一个解决特定问题的小工具&#xff0c;让他成为其他开发者手中的利器&#xff1f;今天&#xff0c;我们就来一场实战训练&#xff0c;学习如何将你的…...

C# 访问Access存取图片

图片存入ole字段&#xff0c;看有的代码是获取图片的字节数组转换为base64字符串&#xff0c;存入数据库&#xff1b;显示图片是把base64字符串转换为字节数组再显示&#xff1b;直接存字节数组可能还好一点&#xff1b; 插入的时候用带参数的sql写法比较好&#xff1b;用拼接…...

正则表达式中常见字符的用法介绍

正则表达式&#xff08;Regular Expression&#xff0c;简称Regex&#xff09;是一种文本模式描述的方法&#xff0c;包括普通字符&#xff08;如a到z之间的字母&#xff09;和特殊字符&#xff08;称为“元字符”&#xff09;。正则表达式使用单个字符串来描述、匹配一系列符合…...

Vue3.0组合式API:依赖注入provide和inject实现跨层组件的通信

Vue3.0组合式API系列文章&#xff1a; 《Vue3.0组合式API&#xff1a;setup()函数》 《Vue3.0组合式API&#xff1a;使用reactive()、ref()创建响应式代理对象》 《Vue3.0组合式API&#xff1a;computed计算属性、watch监听器、watchEffect高级监听器》 《Vue3.0组合式API&…...

VSCode中配置C/C++环境

在Visual Studio Code&#xff08;VSCode&#xff09;中配置C/C环境是一个相对直接且功能强大的过程&#xff0c;它能让开发者利用VSCode的诸多便利功能来编写、编译和调试C/C代码。以下是一个详细的步骤指南&#xff0c;涵盖了从安装必要的软件到配置编译器、调试器以及VSCode…...

vue实现鼠标滚轮控制页面横向滑动

先看效果 20240919_095531 1.首先创建一个xScroll.vue组件 <template><div class"main" v-size-ob"mainSize"><div class"v-scroll"><div class"content"><slot></slot></div></div>…...

【Git使用】删除Github仓库中的指定文件/文件夹

前言&#xff1a; 上篇文章带大家上传了第一个项目至github,那要是想删除仓库中的指定文件夹怎么办&#xff1f;在Github中 仓库是无法通过鼠标操作直接删除文件和文件夹的&#xff0c;那只能通过 git 命令来执行删除操作。接下来就带大家进行操作。 详细步骤&#xff1a; 一…...

Iptables命令常用命令

前言:下是一些非常实用的 iptables 命令合集&#xff0c;涵盖网络攻击防护和日常网络安全防护 1. 查看当前规则 iptables -L -v -n查看现有的所有规则&#xff0c;-v 显示详细信息&#xff0c;-n 禁止解析IP地址和端口以加快显示速度。 2. 清空所有规则 iptables -F清除所有已…...

前端开发之原型模式

介绍 原型模式本质就是借用一个已有的实例做原型&#xff0c;在这原型基础上快速复制出一个和原型一样的一个对象。 class CloneDemo {name clone democlone(): CloneDemo {return new CloneDemo()} } 原型原型链 函数&#xff08;class&#xff09;都有显示原型 prototyp…...

分布式缓存服务Redis版解析与配置方式

一、Redis分布式缓存服务概述 Redis是一款高性能的键值对&#xff08;Key-Value&#xff09;存储系统&#xff0c;通常用作分布式缓存服务。它基于内存运行&#xff0c;支持丰富的数据类型&#xff0c;并具备高并发、低延迟的特点&#xff0c;非常适合用于缓存需要频繁访问的数…...

WordPress建站钩子函数及使用

目录 前言&#xff1a; 使用场景&#xff1a; 一、常用的wordpress钩子&#xff08;动作钩子、过滤器钩子&#xff09; 1、动作钩子&#xff08;Action Hooks&#xff09; 2、过滤器钩子&#xff08;Filter Hooks&#xff09; 二、常用钩子示例 1、添加自定义 CSS 和 JS…...

Qt 模型视图(二):模型类QAbstractItemModel

文章目录 Qt 模型视图(二)&#xff1a;模型类QAbstractItemModel1.基本概念1.1.模型的基本结构1.2.模型索引1.3.行号和列号1.4.父项1.5.项的角色1.6.总结 Qt 模型视图(二)&#xff1a;模型类QAbstractItemModel ​ 模型/视图结构是一种将数据存储和界面展示分离的编程方法。模…...

算法打卡 Day41(动态规划)-理论基础 + 斐波那契数 + 爬楼梯 + 使用最小花费爬楼梯

文章目录 理论基础Leetcode 509-斐波那契数题目描述解题思路 Leetcode 70-爬楼梯题目描述解题思路 Leetcode 746-用最小花费爬楼梯题目描述解题思路 理论基础 动态规划&#xff0c;简称 DP&#xff0c;其中的每一个状态一定是由上一个状态推导出来的&#xff0c;而贪心算法没有…...

鸿蒙环境服务端签名直传文件到OSS

本文介绍如何在鸿蒙环境下将文件上传到OSS。 背景信息 鸿蒙环境是当下比较流行的操作环境&#xff0c;与服务端签名直传的原理类似&#xff0c;鸿蒙环境上传文件到OSS是利用OSS提供的PutObject接口来实现文件上传到OSS。关于PutObject的详细介绍&#xff0c;请参见PutObject。…...

计算机毕业设计Python+Flask微博情感分析 微博舆情预测 微博爬虫 微博大数据 舆情分析系统 大数据毕业设计 NLP文本分类 机器学习 深度学习 AI

首先安装需要的python库&#xff0c; 安装完之后利用navicat导入数据库文件bili100.sql到mysql中&#xff0c; 再在pycharm编译器中连接mysql数据库&#xff0c;并在设置文件中将密码修改成你的数据库密码。最后运行app.py&#xff0c;打开链接&#xff0c;即可运行。 B站爬虫数…...

铭豹扩展坞 USB转网口 突然无法识别解决方法

当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…...

线程与协程

1. 线程与协程 1.1. “函数调用级别”的切换、上下文切换 1. 函数调用级别的切换 “函数调用级别的切换”是指&#xff1a;像函数调用/返回一样轻量地完成任务切换。 举例说明&#xff1a; 当你在程序中写一个函数调用&#xff1a; funcA() 然后 funcA 执行完后返回&…...

转转集团旗下首家二手多品类循环仓店“超级转转”开业

6月9日&#xff0c;国内领先的循环经济企业转转集团旗下首家二手多品类循环仓店“超级转转”正式开业。 转转集团创始人兼CEO黄炜、转转循环时尚发起人朱珠、转转集团COO兼红布林CEO胡伟琨、王府井集团副总裁祝捷等出席了开业剪彩仪式。 据「TMT星球」了解&#xff0c;“超级…...

C++ 求圆面积的程序(Program to find area of a circle)

给定半径r&#xff0c;求圆的面积。圆的面积应精确到小数点后5位。 例子&#xff1a; 输入&#xff1a;r 5 输出&#xff1a;78.53982 解释&#xff1a;由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982&#xff0c;因为我们只保留小数点后 5 位数字。 输…...

HTML前端开发:JavaScript 常用事件详解

作为前端开发的核心&#xff0c;JavaScript 事件是用户与网页交互的基础。以下是常见事件的详细说明和用法示例&#xff1a; 1. onclick - 点击事件 当元素被单击时触发&#xff08;左键点击&#xff09; button.onclick function() {alert("按钮被点击了&#xff01;&…...

C++八股 —— 单例模式

文章目录 1. 基本概念2. 设计要点3. 实现方式4. 详解懒汉模式 1. 基本概念 线程安全&#xff08;Thread Safety&#xff09; 线程安全是指在多线程环境下&#xff0c;某个函数、类或代码片段能够被多个线程同时调用时&#xff0c;仍能保证数据的一致性和逻辑的正确性&#xf…...

selenium学习实战【Python爬虫】

selenium学习实战【Python爬虫】 文章目录 selenium学习实战【Python爬虫】一、声明二、学习目标三、安装依赖3.1 安装selenium库3.2 安装浏览器驱动3.2.1 查看Edge版本3.2.2 驱动安装 四、代码讲解4.1 配置浏览器4.2 加载更多4.3 寻找内容4.4 完整代码 五、报告文件爬取5.1 提…...

均衡后的SNRSINR

本文主要摘自参考文献中的前两篇&#xff0c;相关文献中经常会出现MIMO检测后的SINR不过一直没有找到相关数学推到过程&#xff0c;其中文献[1]中给出了相关原理在此仅做记录。 1. 系统模型 复信道模型 n t n_t nt​ 根发送天线&#xff0c; n r n_r nr​ 根接收天线的 MIMO 系…...

基于Java+VUE+MariaDB实现(Web)仿小米商城

仿小米商城 环境安装 nodejs maven JDK11 运行 mvn clean install -DskipTestscd adminmvn spring-boot:runcd ../webmvn spring-boot:runcd ../xiaomi-store-admin-vuenpm installnpm run servecd ../xiaomi-store-vuenpm installnpm run serve 注意&#xff1a;运行前…...

AI语音助手的Python实现

引言 语音助手(如小爱同学、Siri)通过语音识别、自然语言处理(NLP)和语音合成技术,为用户提供直观、高效的交互体验。随着人工智能的普及,Python开发者可以利用开源库和AI模型,快速构建自定义语音助手。本文由浅入深,详细介绍如何使用Python开发AI语音助手,涵盖基础功…...