news 2026/9/2 14:25:45

JavaWeb音乐网站实战:Servlet+JSP+MySQL从零搭建完整项目

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
JavaWeb音乐网站实战:Servlet+JSP+MySQL从零搭建完整项目

简介:这是一套基于JavaWeb技术栈开发的小型音乐网站实战项目,面向Java初学者与Web开发入门者,解决在线音乐播放、下载、分类浏览及排行榜展示等核心功能的完整实现问题。资源包共881个文件,涵盖80个JSP页面、60个Java后端类、44个运行依赖JAR包、23首MP3示例音频、以及大量前端资源(116个JPG、222个PNG、22个JS、18个HTML、16个CSS),结构清晰,模块划分明确,便于理解MVC分层设计与前后端交互逻辑。压缩包大小为189.75MB,已提供可直接部署的WAR包、完整源码、全部依赖库及MySQL建表SQL脚本,开箱即用或按需二次开发。目前已有912人学习下载,适合用于课程设计、毕业设计参考或JavaWeb技能巩固训练,尤其有助于掌握Tomcat部署、JDBC数据库操作、文件上传下载及JSP+Servlet基础应用。

1. 项目缘起:为什么从零搭建一个音乐网站依然有价值?

最近在整理硬盘时,翻出了大学时期用JSP+Servlet写的第一个“音乐播放器”,界面简陋,功能单一,但它却是我技术生涯的起点。如今,流媒体平台早已是Spotify、网易云的天下,一个“小型音乐网站”听起来似乎有些过时。但恰恰相反,我认为对于正在学习Java Web开发的同学,或者想深入理解Web应用从数据库到前端完整生命周期的开发者来说,亲手搭建一个这样的项目,其价值远超你的想象。它不是一个简单的“增删改查”练习,而是一个涵盖了用户认证、文件上传与管理、动态内容渲染、前后端数据交互、基础搜索等多个核心Web开发技能的微型综合体。

你可能在搜索“javaweb项目完整案例idea”或“javaweb项目完整案例mysql”,这说明你需要的不是一个孤立的代码片段,而是一个能跑起来、能理解其脉络的完整项目。这个基于JavaWeb的小型音乐网站,正是这样一个案例。它不追求商业级的复杂度和性能,而是聚焦于如何将JSP、Servlet、JDBC、MySQL这些经典技术栈有机地串联起来,解决一个具体的问题:如何让用户上传、管理和聆听音乐。在这个过程中,你会遇到并解决文件存储路径设计、音频文件元信息(如时长、歌手)的获取、播放列表的会话管理、以及如何在前端HTML页面中优雅地嵌入和控制音频播放器等一系列真实问题。接下来,我将以一个“过来人”的身份,带你从零开始,用IDEA和MySQL,一步步构建这个属于你自己的音乐角落。

2. 技术选型与项目骨架搭建:为什么是这套“经典组合拳”?

在开始敲代码之前,明确技术栈是至关重要的一步。对于这个项目,我选择了最经典、也最利于学习的Java Web技术组合。这套组合可能不是最“潮”的,但绝对是理解Web开发底层逻辑最扎实的路径。

2.1 后端核心:Servlet + JSP + JDBC

后端我们使用Servlet作为控制器(Controller),JSP作为视图(View),JDBC直接操作数据库。为什么不直接用Spring Boot?因为Spring Boot做了大量的封装,对于初学者而言,就像直接开上了一辆自动挡的跑车,虽然快,但你可能不知道离合器、变速箱是如何工作的。而Servlet和JSP能让你清晰地看到HTTP请求如何被接收、处理,以及HTML页面是如何动态生成的。这是理解MVC模式(尽管我们这里是比较原始的Model 2)的绝佳起点。

JDBC的选择同样出于教学目的。你会亲手编写SQL语句,建立数据库连接池(这是必须的,后面会详述),处理ResultSet。这个过程会让你对数据库操作、SQL注入防护(使用PreparedStatement)有肌肉记忆般的理解。等未来你使用MyBatis或JPA时,你会更感激这段“徒手造轮子”的经历。

2.2 前端呈现:原生HTML/CSS/JS + JSTL

前端层面,我们主要使用原生的HTML、CSS和JavaScript。JSP页面负责渲染动态数据,而为了在JSP中避免使用Java脚本片段(<% ... %>),我们会引入JSTL标签库和EL表达式。这不仅是规范,更能让你的JSP页面看起来更干净,逻辑更清晰。例如,遍历歌曲列表将从原始的for循环变为优雅的<c:forEach>标签。

音频播放是核心功能。我们将使用HTML5的<audio>标签,并通过JavaScript来控制播放、暂停、切换歌曲以及更新播放进度条。这里会涉及到如何将后端传来的歌曲文件路径(URL)正确地设置到audio元素的src属性上。

2.3 开发环境与工具清单

  • IDE: IntelliJ IDEA Ultimate版(学生可免费申请)或Community版配合Tomcat插件。IDEA对Java Web项目的支持远超Eclipse,尤其是热部署和调试。
  • Web服务器: Apache Tomcat 9.x。稳定且兼容性好,是学习Servlet规范的标准环境。
  • 数据库: MySQL 8.0 或 5.7。我们将手动设计表结构,并编写建表SQL。
  • 项目构建与管理: 使用Maven。即使项目小,也强烈建议使用Maven来管理依赖(如数据库驱动、JSTL、连接池库),它能让你摆脱手动拷贝jar包的混乱。
  • 版本控制: Git。从第一天就初始化Git仓库,养成良好的版本管理习惯。

注意:确保你的IDEA中已正确配置了Tomcat服务器和Maven。这是一个常见的起步卡点,如果遇到“Application Server was not connected before run configuration stop”这类错误,通常是因为Tomcat路径配置有误或端口冲突。

3. 数据库设计与核心表结构解析

数据库是应用的基石,设计的好坏直接影响到后续开发的复杂度和性能。我们的音乐网站核心实体是“用户”和“音乐”,它们之间的关系是“用户上传/收藏音乐”。此外,我们还需要考虑“播放列表”这种动态集合。这里我提供一个经过实践检验的简化版设计。

3.1 核心表字段定义与关系

我们创建四张表:

  1. 用户表 (user)存储用户的基本信息和认证凭证。

    CREATE TABLE `user` ( `id` int(11) NOT NULL AUTO_INCREMENT, `username` varchar(50) NOT NULL UNIQUE COMMENT '用户名,用于登录和显示', `password` varchar(255) NOT NULL COMMENT '密码,存储BCrypt加密后的密文', `email` varchar(100) DEFAULT NULL, `avatar` varchar(500) DEFAULT NULL COMMENT '头像图片的存储路径', `create_time` datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表';

    关键点password字段必须存储加密后的密码,绝对禁止明文存储。我们将使用BCrypt算法,这是目前存储密码的行业标准。username设唯一约束,防止重复注册。

  2. 音乐表 (music)存储音乐文件的元数据信息,不存储文件本身。

    CREATE TABLE `music` ( `id` int(11) NOT NULL AUTO_INCREMENT, `title` varchar(200) NOT NULL COMMENT '歌曲标题', `singer` varchar(100) DEFAULT NULL COMMENT '歌手', `album` varchar(200) DEFAULT NULL COMMENT '专辑', `duration` int(11) DEFAULT NULL COMMENT '时长,单位:秒', `file_path` varchar(500) NOT NULL COMMENT '音乐文件在服务器上的存储路径(相对路径)', `uploader_id` int(11) NOT NULL COMMENT '上传者ID,关联user.id', `upload_time` datetime DEFAULT CURRENT_TIMESTAMP, `play_count` int(11) DEFAULT '0' COMMENT '播放次数,用于热门排序', PRIMARY KEY (`id`), KEY `idx_uploader` (`uploader_id`), KEY `idx_title_singer` (`title`,`singer`) COMMENT '复合索引,用于搜索优化', CONSTRAINT `fk_music_uploader` FOREIGN KEY (`uploader_id`) REFERENCES `user` (`id`) ON DELETE CASCADE ) FOREIGN KEY约束保证了数据完整性,删除用户时,其上传的音乐也会被删除。 ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='音乐信息表';

    关键点

    • file_path:这里存储的是相对路径,如/uploads/music/xxx.mp3。绝对路径会因服务器环境变化而失效,相对路径更灵活。我们需要在Servlet中通过getServletContext().getRealPath()来将其转换为绝对路径进行文件读写。
    • duration:如何获取?这需要在文件上传时,通过后端Java代码解析MP3文件的ID3标签或使用第三方库(如jaudiotagger)来读取时长信息,并存入数据库。这是一个很好的实践点。
    • 索引:为uploader_id(title, singer)创建索引,能显著提升根据上传者查询和根据歌名/歌手搜索的性能。
  3. 收藏表 (favorite)记录用户收藏音乐的关系,是多对多关系的中间表。

    CREATE TABLE `favorite` ( `user_id` int(11) NOT NULL, `music_id` int(11) NOT NULL, `create_time` datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`user_id`,`music_id`), -- 联合主键,防止重复收藏 KEY `idx_music` (`music_id`), CONSTRAINT `fk_fav_user` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) ON DELETE CASCADE, CONSTRAINT `fk_fav_music` FOREIGN KEY (`music_id`) REFERENCES `music` (`id`) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='收藏关系表';
  4. 播放列表表 (playlist)一个简单的播放列表,允许用户创建自己的歌单。这里设计为每个播放列表属于一个用户,包含多首音乐。

    CREATE TABLE `playlist` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(100) NOT NULL, `user_id` int(11) NOT NULL, `create_time` datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), KEY `idx_user` (`user_id`), CONSTRAINT `fk_playlist_user` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='播放列表表';

    播放列表与音乐的关联:这需要另一张中间表playlist_music,结构类似于favorite表,包含playlist_idmusic_id。为了简化初始版本,我们可以先实现“当前会话播放列表”,即利用HttpSession在服务器内存中临时存储用户当前播放的歌曲ID列表,这样无需建表。待核心功能稳定后再扩展持久化播放列表。

3.2 数据库连接池:为什么不用DriverManager?

在Servlet中直接使用DriverManager.getConnection()是初学者常见做法,但这是严重错误的。因为每次请求都创建和关闭数据库连接,开销巨大,会迅速拖垮你的应用。正确的做法是使用数据库连接池,如HikariCP(目前性能最好的连接池之一)。

pom.xml中引入依赖后,你需要配置一个DataSource。通常,我们会创建一个工具类DBUtil,在其中初始化HikariCP数据源,并提供静态方法getConnection()。这个连接池会在应用启动时创建一批连接备用,用时取出,用毕归还,极大地提升了性能。

// 示例:DBUtil工具类核心部分 public class DBUtil { private static final HikariDataSource dataSource; static { HikariConfig config = new HikariConfig(); config.setJdbcUrl("jdbc:mysql://localhost:3306/music_db?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai"); config.setUsername("root"); config.setPassword("your_password"); config.setMaximumPoolSize(10); // 根据实际情况调整 config.setMinimumIdle(5); dataSource = new HikariDataSource(config); } public static Connection getConnection() throws SQLException { return dataSource.getConnection(); } // ... 关闭资源的方法 }

4. 核心功能Servlet实现与业务逻辑拆解

有了数据库设计,我们就可以开始编写处理HTTP请求的Servlet了。我们将按照功能模块来组织Servlet。

4.1 用户认证模块:登录、注册与会话管理

注册Servlet (RegisterServlet)

  1. doPost方法中获取username,password,email参数。
  2. 校验:检查用户名是否已存在(查询数据库)。
  3. 加密:使用BCryptPasswordEncoder对明文密码进行哈希加密。永远不要自己写加密算法!
  4. 入库:将加密后的密码、用户名、邮箱插入user表。
  5. 跳转:注册成功则重定向到登录页面,失败则转发回注册页并携带错误信息。

登录Servlet (LoginServlet)

  1. 获取usernamepassword
  2. 根据用户名从数据库查询用户信息。
  3. 使用BCryptPasswordEncoder.matches(rawPassword, encodedPassword)方法验证密码。这是关键,因为BCrypt每次加密结果都不同,只能通过matches方法比对。
  4. 验证通过后,将用户对象(至少包含id和username)存入HttpSession中:request.getSession().setAttribute("user", user)
  5. 登录成功重定向到首页(如index.jsp)。

登录状态检查与拦截我们需要一个过滤器AuthFilter,对需要登录才能访问的路径(如/upload,/favorite)进行拦截。在doFilter方法中,检查Session里是否存在user属性,如果不存在,则重定向到登录页。

@WebFilter("/*") // 过滤所有请求,在内部判断哪些路径需要拦截 public class AuthFilter implements Filter { private final List<String> whiteList = Arrays.asList("/login", "/register", "/css/", "/js/", "/uploads/"); @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest req = (HttpServletRequest) request; HttpServletResponse resp = (HttpServletResponse) response; String path = req.getRequestURI().substring(req.getContextPath().length()); // 检查是否在白名单(静态资源、登录注册页) boolean isWhite = whiteList.stream().anyMatch(path::startsWith); if (!isWhite) { HttpSession session = req.getSession(false); // 不创建新session if (session == null || session.getAttribute("user") == null) { resp.sendRedirect(req.getContextPath() + "/login.jsp"); return; // 拦截,不再执行后续过滤器或Servlet } } chain.doFilter(request, response); // 放行 } }

4.2 音乐文件上传与管理模块

这是项目的难点和重点,涉及文件I/O和数据库事务。

上传Servlet (UploadServlet)

  1. 必须将Servlet注解设置为支持文件上传:@MultipartConfig
  2. doPost中,通过request.getPart("musicFile")获取上传的文件Part对象。musicFile是前端<input type="file" name="musicFile">中的name。
  3. 文件存储
    • 生成唯一文件名:使用UUID.randomUUID().toString()+ 原文件后缀,防止覆盖。
    • 确定存储目录:通常放在Web应用外的独立目录,如D:/music_uploads/,或者Web应用下的/WEB-INF/uploads(后者更安全,无法直接通过URL访问)。这里建议使用外部目录,并通过Servlet提供文件下载流。
    • 将Part的输入流写入到目标文件:part.write(absoluteFilePath)
  4. 元信息提取与入库
    • 同时获取表单的其他字段:title,singer,album
    • 解析时长:调用一个工具方法,使用jaudiotagger库读取刚保存的MP3文件,获取时长(秒)。
    • 获取当前登录用户ID(从Session中)。
    • 组装Music对象,包含file_path(存储相对路径或用于访问的虚拟路径)、titlesingeralbumdurationuploader_id
    • 执行数据库插入操作。这里要考虑事务:如果入库失败,应该尝试删除已上传的文件,保持数据一致性。但简单起见,初期可以暂不处理,但心里要有这根弦。
  5. 结果返回:成功则重定向到“我的上传”列表页,失败则返回错误信息。

踩坑实录:文件上传大小限制。默认情况下,Servlet对上传文件大小有限制。如果用户上传超大文件,会抛出异常。你需要在@MultipartConfig注解中设置maxFileSizemaxRequestSize属性,例如@MultipartConfig(maxFileSize = 50 * 1024 * 1024, maxRequestSize = 100 * 1024 * 1024)表示最大文件50MB,最大请求100MB。同时,前端最好也通过JS做初步校验,提升用户体验。

音乐文件访问服务文件存储在服务器硬盘上,如何让前端<audio>src能够播放它?你不能直接给一个硬盘路径。我们需要一个专门的FileServletMusicStreamServlet

  1. 这个Servlet接收一个参数,如id(音乐ID)或path(文件路径)。
  2. 根据ID从数据库查出file_path,或直接使用安全的路径参数(必须防止路径遍历攻击,如../../../etc/passwd)。
  3. 设置响应头:resp.setContentType("audio/mpeg");或根据文件后缀动态设置。
  4. 设置缓存头(可选):resp.setHeader("Cache-Control", "max-age=3600");对音频文件缓存可以节省带宽。
  5. 将文件内容通过Files.copy(Paths.get(fileAbsolutePath), resp.getOutputStream())写入响应输出流。

这样,前端音频标签的src就可以设置为/music/stream?id=123

4.3 音乐播放与列表管理

首页/音乐列表Servlet (IndexServlet)

  1. 处理分页参数:page(当前页),size(每页条数,如20)。
  2. 编写SQL查询音乐列表,按上传时间或播放次数排序。使用LIMIT (page-1)*size, size实现分页。
  3. 计算总记录数,用于生成分页导航。
  4. 将查询到的List<Music>和分页信息存入Request属性:request.setAttribute("musicList", list);
  5. 转发到index.jsp进行渲染。

播放与播放列表(会话级)

  1. PlayServlet中,接收歌曲ID。
  2. 将歌曲ID添加到当前用户的Session作用域的播放列表(一个List<Integer>)中。如果已存在则不重复添加。
  3. 可以同时增加该歌曲的play_count
  4. 返回JSON响应,告知前端操作成功,或者直接重定向到播放器页面。
  5. 前端播放器页面(player.jsp)从Session中获取播放列表的ID,然后通过AJAX动态加载这些歌曲的详细信息(标题、歌手、文件URL),并利用JavaScript控制<audio>标签按顺序或随机播放。

收藏功能实现类似播放列表,但数据是持久化到favorite表中的。需要AddFavoriteServletRemoveFavoriteServlet,以及一个FavoriteListServlet来查询用户收藏的音乐。

5. 前端页面交互与关键实现细节

前端页面是用户直接交互的地方,良好的体验至关重要。

5.1 使用JSTL与EL渲染动态列表

index.jsp中,我们将避免使用Scriptlet。假设Servlet已经将musicList放入了request作用域。

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> ... <table> <c:forEach var="music" items="${requestScope.musicList}"> <tr> <td>${music.title}</td> <td>${music.singer}</td> <td> <!-- 播放按钮,触发JS函数,将歌曲加入播放列表并播放 --> <button onclick="playMusic(${music.id})">播放</button> <!-- 收藏按钮,判断当前歌曲是否已被用户收藏 --> <c:set var="isFav" value="false" /> <c:forEach var="favId" items="${sessionScope.favIds}"> <c:if test="${favId eq music.id}"><c:set var="isFav" value="true" /></c:if> </c:forEach> <button onclick="toggleFavorite(${music.id}, this)" class="${isFav ? 'fav-active' : ''}"> ${isFav ? '已收藏' : '收藏'} </button> </td> </tr> </c:forEach> </table>

5.2 音频播放器的控制逻辑

播放器页面player.jsp的核心是一个隐藏的<audio>标签和一个控制面板。

<audio id="audioPlayer" controls style="width:100%;" ontimeupdate="updateProgress()" onended="playNext()"> 您的浏览器不支持 audio 元素。 </audio> <div> <button onclick="playPause()">播放/暂停</button> <span id="currentTime">0:00</span> / <span id="totalTime">0:00</span> <input type="range" id="progressBar" value="0" max="100" oninput="seekTo(this.value)"> </div> <ul id="playlist"></ul>

JavaScript逻辑:

  1. playMusic(musicId)函数:首先通过fetch调用/play?id=xxx将歌曲加入会话播放列表,然后获取该歌曲的播放URL,将其设置为audioPlayer.src,并调用audioPlayer.play()
  2. updateProgress()函数:在audioontimeupdate事件中触发,计算当前播放进度百分比,更新progressBarcurrentTime显示。
  3. seekTo(percent)函数:当用户拖动进度条时,根据百分比计算新的播放时间audioPlayer.currentTime
  4. playNext()函数:在onended事件中触发,从Session播放列表中获取下一首歌曲的ID,重复步骤1。

5.3 实现基础搜索功能

搜索功能可以做得简单或复杂。一个基础的实现是在首页放置一个搜索框。

  1. 表单提交到SearchServlet,参数为keyword
  2. 在Servlet中,构造SQL语句:SELECT * FROM music WHERE title LIKE ? OR singer LIKE ?。使用PreparedStatement,参数设置为%keyword%,防止SQL注入。
  3. 将搜索结果列表存入request,转发到searchResult.jsp进行展示。

关于“javaweb高德关键字查询在html页面怎么用”的联想:这个热词提示了更高级的搜索场景——结合地理位置。虽然我们的音乐网站不需要,但思路可以借鉴。例如,如果你想做一个“寻找附近音乐会”的功能,就需要存储场馆的经纬度,然后根据用户当前位置(通过浏览器HTML5 Geolocation API获取),使用高德地图的Web服务API进行周边搜索,再将结果展示在页面上。这属于前后端分离更深入的集成,核心是前端获取坐标后传给后端,后端调用高德接口,处理返回的JSON数据。

6. 项目部署、优化与扩展思考

当你在本地IDEA里用Tomcat跑通整个项目后,下一步就是考虑如何让它更像一个“真正”的网站。

6.1 部署到外部Tomcat

  1. 在IDEA中,使用Maven的package命令生成WAR文件:music_website.war
  2. 将WAR文件拷贝到独立Tomcat的webapps目录下。
  3. 启动Tomcat,它会自动解压WAR包并部署应用。
  4. 关键配置
    • 数据库连接池:现在你的数据库配置(URL、用户名、密码)硬编码在DBUtil里。在生产环境中,应该放在Tomcat的context.xml或外部的properties文件中,通过JNDI来获取DataSource。这是企业级应用的标准做法。
    • 文件上传路径:在外部Tomcat中,getServletContext().getRealPath("/")获取的路径可能每次部署都变。因此,文件存储的根目录最好在web.xml中配置为一个绝对路径的初始化参数,或者使用系统环境变量。

6.2 性能与体验优化点

  1. 静态资源缓存:对CSS、JS、图片等配置Tomcat的静态资源缓存,减少请求。
  2. 数据库查询优化:对频繁查询的字段建立索引(我们之前已经做了)。对于复杂的首页列表,考虑引入缓存,如Redis,将热门歌曲列表缓存起来,定时更新。
  3. 前端懒加载与分页:列表页一定要做分页,避免一次性加载成千上万条数据。可以进一步实现“滚动加载”。
  4. 音频预加载:可以在播放当前歌曲时,悄悄预加载下一首歌曲的少量数据,减少切换等待时间。

6.3 可能的扩展方向

这个小型网站是一个完美的起点,你可以在此基础上添加更多功能,深化对特定技术的理解:

  • 音乐推荐:基于用户的收藏、播放记录,实现一个简单的协同过滤推荐算法。
  • 评论与社交:增加歌曲评论、用户关注、动态分享功能。
  • 后台管理系统:为管理员提供一个独立的界面,管理用户、审核上传的音乐、查看统计数据。
  • 迁移到Spring Boot:当你对Servlet/JSP这套流程烂熟于心后,可以尝试用Spring Boot重构这个项目。你会瞬间理解Spring MVC的@Controller@RequestMappingJdbcTemplateMyBatis带来的巨大便利,这种对比学习的效果极佳。
  • 前后端分离:将后端改造成纯RESTful API(使用Spring Boot + Spring Security + JWT),前端使用Vue.js或React重写。这是现代Web开发的主流架构,理解其与传统MVC的差异至关重要。

从零搭建一个完整的JavaWeb应用,就像完成一次精密的拼装。你会遇到乱码问题、空指针异常、文件权限问题、SQL错误、前端JS调试等一系列挑战。每一个问题的解决,都是你技术栈上坚实的一块砖。这个小型音乐网站项目,麻雀虽小五脏俱全,它带给你的不仅仅是几行代码,更是对Web应用骨架的深刻认知。当你看到自己上传的音乐在浏览器中响起时,那种成就感,是任何现成项目都无法比拟的。开始动手吧,遇到问题,正是学习的开始。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/2 14:20:10

STM32C5A3R入门实战:从零搭建开发环境到GPIO控制LED闪烁

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/2 14:19:31

Grok Bot接入特斯拉车机系统:移动AI工作站的工程实现指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/2 14:18:27

100 行把 UI 接进 raylib:raygui 即时模式控件实战

100 行把 UI 接进 raylib&#xff1a;raygui 即时模式控件实战 【免费下载链接】raylib A simple and easy-to-use library to enjoy videogames programming 项目地址: https://gitcode.com/GitHub_Trending/ra/raylib raylib 项目里画 2D、3D 场景的人很多&#xff0c…

作者头像 李华
网站建设 2026/9/2 14:17:43

小空间嵌入式空调怎么选?无外机打孔与安装避坑指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/2 14:14:02

通用智能的本质是适应而非预设:Agent与RAG的架构实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华