ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

JavaWeb音乐网站实战:Servlet/JSP+MVC架构实现用户管理与在线播放

JavaWeb音乐网站实战:Servlet/JSP+MVC架构实现用户管理与在线播放 简介这是一套面向Java Web初学者与课程设计者的完整音乐网站实战项目基于SSMSpringSpringMVCMyBatis技术栈实现核心功能解决在线音乐播放、下载、分类浏览与热门排行等典型Web应用需求。资源包共881个文件包含80个JSP页面、60个Java业务类、44个Jar依赖库、23首MP3示例音频、百余张UI图片PNG/JPG/GIF及配套CSS/JS/HTML前端资源整体压缩包达189.75MB结构清晰、模块分明便于理解MVC分层与前后端交互逻辑。已有912人学习下载适合用于Java Web课程实训、毕业设计参考或快速搭建音乐类原型系统。所有源码均已编译打包为WAR文件可直接部署至Tomcat运行MySQL建库脚本与数据库配置齐全附带全部依赖Jar包与开发环境说明EclipseJDKTomcat开箱即用亦支持按需二次开发与功能扩展。1. 项目概述与核心价值最近在整理过去的项目资料翻到了一个几年前做的基于JavaWeb的小型音乐网站。这个项目虽然体量不大但麻雀虽小五脏俱全涵盖了用户管理、音乐上传、在线播放、歌单管理、后台数据统计等核心功能。对于刚学完JavaWeb基础Servlet、JSP、JDBC想找个完整项目练手的朋友或者想快速搭建一个个人音乐收藏站点的开发者来说这个案例的参考价值非常高。它不像那些动辄微服务、分布式的大项目而是聚焦于如何用最经典、最直接的JavaWeb技术栈解决一个具体场景下的实际问题。今天我就把这个项目的完整实现思路、关键代码、踩过的坑以及部署上线的经验系统地梳理一遍希望能帮你少走弯路快速搭建起自己的第一个音乐网站。这个项目本质上是一个内容管理与分享平台。用户可以在线试听音乐收藏自己喜欢的歌曲到个人歌单管理员则负责维护音乐库上传音频文件、编辑歌曲信息并查看网站的访问数据。技术选型上我们采用经典的MVC三层架构前端用JSPJSTL一点原生JavaScript和CSS当时还没怎么用框架后端用Servlet处理业务逻辑数据持久化层用JDBC直连MySQL。开发工具是IntelliJ IDEA这也是目前Java开发的主流IDE配合Maven进行依赖管理。整个项目的代码结构清晰业务逻辑完整非常适合作为从理论学习到项目实战的过渡桥梁。2. 项目整体设计与技术栈选型2.1 为什么选择经典JavaWeb技术栈在开始动手之前我们先聊聊技术选型。现在前端有Vue、React后端有Spring Boot为什么还要用“老掉牙”的Servlet/JSP我的考虑主要有三点。第一学习曲线平滑。对于JavaWeb初学者Servlet和JSP是理解Web请求/响应模型、会话管理、数据库操作等核心概念的基石。跳过它们直接上框架容易变成“配置工程师”遇到问题不知其所以然。第二轻量且可控。这个项目功能明确规模小引入庞大的Spring全家桶反而显得臃肿。纯Servlet开发没有复杂的依赖注入和AOP整个项目的启动速度和内存占用都更有优势你对每一行代码的控制力也更强。第三部署简单。最终打包成一个WAR文件扔到Tomcat等Servlet容器里就能跑几乎零配置非常适合个人项目快速部署验证。当然我们也会用上一些提升开发体验的“现代”组件。比如使用Maven来管理项目依赖如数据库驱动、连接池、文件上传组件而不是手动下载jar包。使用Druid作为数据库连接池替代传统的DriverManager能显著提升数据库访问性能和稳定性。使用Commons FileUpload来处理文件上传比手动解析multipart/form-data请求体要方便可靠得多。这些组件的引入是在保持核心架构简洁的同时对开发效率和项目健壮性的必要增强。2.2 系统架构与数据库设计整个项目采用典型的三层架构表示层View、控制层Controller、模型层Model和数据访问层DAO。表示层View由JSP页面构成负责数据的展示和用户交互。我们会尽量使用JSTL标签和EL表达式来替代JSP中的Java脚本% ... %使页面更清晰符合MVC的分离思想。控制层Controller由多个Servlet充当。每个Servlet对应一个或一组相关的业务请求如LoginServlet处理登录MusicServlet处理歌曲的增删改查。Servlet接收请求参数调用相应的业务逻辑Service层然后根据结果转发Forward或重定向Redirect到不同的JSP页面。业务逻辑层Service封装核心业务规则和流程。例如用户注册时检查用户名是否重复、密码加密上传音乐时校验文件格式、保存文件到指定目录并写入数据库记录。这一层是项目的“大脑”。数据访问层DAO提供对数据库的原子操作如UserDao包含findByUsername、insert等方法MusicDao包含findAll、findById、insert等方法。它只关心SQL的执行和结果集的封装不涉及任何业务逻辑。数据库设计是项目的基石。我们设计了几张核心表用户表 (user)存储用户基本信息。CREATE TABLE user ( id int(11) NOT NULL AUTO_INCREMENT, username varchar(50) NOT NULL UNIQUE COMMENT 用户名唯一, password varchar(255) NOT NULL COMMENT 加密后的密码, email varchar(100) DEFAULT NULL, avatar varchar(255) DEFAULT /static/images/default_avatar.png COMMENT 头像路径, create_time datetime DEFAULT CURRENT_TIMESTAMP, is_admin tinyint(1) DEFAULT 0 COMMENT 是否是管理员0-否1-是, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;注意密码字段一定要用varchar(255)为存储加密后的长字符串预留空间。绝对不要明文存储密码。音乐表 (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 专辑, release_time year(4) DEFAULT NULL COMMENT 发行年份, duration int(11) DEFAULT NULL COMMENT 时长秒, file_path varchar(500) NOT NULL COMMENT 音乐文件服务器存储路径, cover_path varchar(500) DEFAULT /static/images/default_cover.jpg 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_singer (singer), KEY idx_uploader (uploader_id), CONSTRAINT fk_music_uploader FOREIGN KEY (uploader_id) REFERENCES user (id) ON DELETE CASCADE ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;实操心得file_path和cover_path存储的是相对于网站根目录或某个特定资源目录的路径而不是绝对路径。这样在迁移服务器时更方便。play_count字段用于实现简单的“热门歌曲”排行。歌单表 (playlist)和歌单-歌曲关联表 (playlist_music)实现用户自定义歌单功能。CREATE TABLE playlist ( id int(11) NOT NULL AUTO_INCREMENT, name varchar(100) NOT NULL, description text, creator_id int(11) NOT NULL COMMENT 创建者ID关联user.id, create_time datetime DEFAULT CURRENT_TIMESTAMP, is_public tinyint(1) DEFAULT 1 COMMENT 是否公开0-私密1-公开, PRIMARY KEY (id), KEY idx_creator (creator_id), CONSTRAINT fk_playlist_creator FOREIGN KEY (creator_id) REFERENCES user (id) ON DELETE CASCADE ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE playlist_music ( playlist_id int(11) NOT NULL, music_id int(11) NOT NULL, add_time datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (playlist_id,music_id), KEY idx_music (music_id), CONSTRAINT fk_pm_playlist FOREIGN KEY (playlist_id) REFERENCES playlist (id) ON DELETE CASCADE, CONSTRAINT fk_pm_music FOREIGN KEY (music_id) REFERENCES music (id) ON DELETE CASCADE ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT歌单与歌曲的多对多关联表;这里使用了一个关联表来实现歌单和歌曲之间的多对多关系。一个歌单可以包含多首歌一首歌也可以被添加到多个歌单中。3. 核心模块实现与难点解析3.1 用户认证与会话管理用户登录是系统的入口。我们实现一个LoginServlet。核心流程接收username和password参数。调用UserService的login方法。在UserService中先根据用户名从数据库查询用户信息。如果用户不存在直接返回失败。将用户输入的密码明文与数据库中存储的加密密码进行比对。这里绝对不能用明文比对如果密码正确将用户对象通常只存放id、username、is_admin等必要信息而非全部字段存入HttpSession中。Servlet根据登录结果跳转到首页成功或回登录页并提示错误失败。密码加密我们使用BCrypt算法。它是目前公认最安全的密码哈希算法之一内置了盐salt机制能有效抵御彩虹表攻击。在Maven中引入bcrypt依赖。// UserService.java 中的密码校验方法 public boolean checkPassword(String inputPassword, String storedHash) { return BCrypt.checkpw(inputPassword, storedHash); } // 注册时加密密码 public String encryptPassword(String rawPassword) { return BCrypt.hashpw(rawPassword, BCrypt.gensalt()); }会话管理注意事项会话超时在web.xml中配置session-configsession-timeout30/session-timeout/session-config单位分钟。登录拦截编写一个LoginFilter过滤器对需要登录才能访问的路径如/user/*,/playlist/*进行拦截。在doFilter方法中检查Session中是否存在用户信息若不存在则重定向到登录页面。安全性登录成功后务必使用session.invalidate()先使旧会话失效再创建新会话request.getSession(true)这可以防止会话固定攻击。3.2 音乐文件上传与存储这是项目的核心功能之一也是容易出问题的地方。我们使用Apache Commons FileUpload组件来处理。Servlet实现要点WebServlet(/upload_music) MultipartConfig(maxFileSize 1024 * 1024 * 50) // 限制单个文件最大50MB public class UploadMusicServlet extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 1. 权限检查从session获取当前用户并检查is_admin标志 User currentUser (User) request.getSession().getAttribute(user); if (currentUser null || !currentUser.getIsAdmin()) { response.sendRedirect(login.jsp); return; } // 2. 获取普通表单字段歌曲名、歌手等 String title request.getParameter(title); String singer request.getParameter(singer); // ... 其他字段 // 3. 获取上传的文件 Part Part audioPart request.getPart(audioFile); // input typefile nameaudioFile Part coverPart request.getPart(coverImage); // 封面图可选 // 4. 文件校验 if (audioPart null || audioPart.getSize() 0) { // 返回错误信息 return; } String audioFileName getSubmittedFileName(audioPart); if (!audioFileName.toLowerCase().endsWith(.mp3)) { // 这里只允许mp3可按需扩展 // 返回错误信息不支持的文件格式 return; } // 5. 生成存储路径和文件名避免重名 String saveDir getServletContext().getRealPath(/static/music); File dir new File(saveDir); if (!dir.exists()) dir.mkdirs(); String savedAudioFileName System.currentTimeMillis() _ UUID.randomUUID() .mp3; String audioFilePath saveDir File.separator savedAudioFileName; // 6. 保存文件到磁盘 audioPart.write(audioFilePath); // 7. 处理封面图类似逻辑 String savedCoverPath /static/images/default_cover.jpg; // 默认封面 if (coverPart ! null coverPart.getSize() 0) { // 校验图片格式保存图片更新savedCoverPath } // 8. 将文件路径相对路径如/static/music/xxx.mp3和歌曲信息存入数据库 Music music new Music(); music.setTitle(title); music.setSinger(singer); music.setFilePath(/static/music/ savedAudioFileName); music.setCoverPath(savedCoverPath); music.setUploaderId(currentUser.getId()); // ... 其他字段 musicService.uploadMusic(music); // 9. 重定向到歌曲列表或成功页面 response.sendRedirect(music_list.jsp); } // 辅助方法用于从Part中获取原始文件名 private static String getSubmittedFileName(Part part) { for (String cd : part.getHeader(content-disposition).split(;)) { if (cd.trim().startsWith(filename)) { String fileName cd.substring(cd.indexOf() 1).trim().replace(\, ); return fileName.substring(fileName.lastIndexOf(/) 1).substring(fileName.lastIndexOf(\\) 1); } } return null; } }踩坑实录文件大小限制除了在MultipartConfig注解中设置还需要在Tomcat的server.xml中配置Connector ... maxPostSize...否则可能遇到请求被拒绝的问题。文件名乱码如果上传的文件名包含中文需要确保Servlet的请求编码request.setCharacterEncoding(UTF-8)已设置并且getSubmittedFileName方法能正确处理。存储路径不要使用绝对路径如C:\project\upload而应使用相对于Web应用根目录的相对路径并通过getServletContext().getRealPath()来获取其在服务器磁盘上的真实路径。这样项目打包部署后路径依然有效。文件名冲突使用时间戳UUID的方式生成最终存储的文件名可以完美避免因用户上传同名文件导致的覆盖问题。3.3 前端音频播放与控制在JSP页面上播放音乐我们使用HTML5的audio标签。这比使用Flash或其他插件要简单和现代得多。基础播放器实现%-- 在music_detail.jsp或歌单页面中 --% audio idaudioPlayer controls preloadmetadata stylewidth: 100%; source src${music.filePath} typeaudio/mpeg 您的浏览器不支持 audio 元素。 /audio div classplayer-controls button onclickdocument.getElementById(audioPlayer).play()播放/button button onclickdocument.getElementById(audioPlayer).pause()暂停/button input typerange idvolumeSlider min0 max1 step0.1 value1 onchangedocument.getElementById(audioPlayer).volumethis.value span idcurrentTime0:00/span / span idduration0:00/span /div script const audioPlayer document.getElementById(audioPlayer); // 更新播放时间 audioPlayer.addEventListener(timeupdate, function() { document.getElementById(currentTime).textContent formatTime(audioPlayer.currentTime); }); // 加载元数据后获取总时长 audioPlayer.addEventListener(loadedmetadata, function() { document.getElementById(duration).textContent formatTime(audioPlayer.duration); // 可选异步请求服务器增加该歌曲的播放次数 fetch(/record_play?musicId${music.id}).catch(e console.log(e)); }); function formatTime(seconds) { const mins Math.floor(seconds / 60); const secs Math.floor(seconds % 60); return ${mins}:${secs 10 ? 0 : }${secs}; } /script播放次数统计当音频元数据加载完成loadedmetadata事件或开始播放play事件时可以向后端发送一个异步请求如使用Fetch API由一个专门的RecordPlayServlet来增加对应歌曲的play_count字段。注意这里需要防刷简单的做法可以结合Session限制同一用户短时间内对同一歌曲的重复计数。歌单播放实现一个简单的播放列表功能。前端可以用一个JavaScript数组存储当前歌单的歌曲ID和播放地址。当一首歌播放结束时监听ended事件自动从数组中取出下一首并更新audioPlayer的src属性然后调用play()方法。3.4 歌单功能与音乐收藏歌单功能涉及多对多关系的增删改查。核心是PlaylistServlet和PlaylistMusicServlet。创建歌单一个POST请求到PlaylistServlet传入歌单名称、描述、是否公开等参数在Service层创建Playlist记录关联当前用户ID。向歌单添加歌曲这是一个典型的关联操作。前端在歌曲列表的每个歌曲项旁提供一个“添加到歌单”按钮点击后弹出一个模态框Modal列出用户自己的所有歌单通过Ajax从PlaylistServlet?actionlistMy获取用户选择后前端发送请求到PlaylistMusicServlet?actionaddplaylistIdxxxmusicIdyyy。后端需要检查1该歌单是否属于当前用户2这首歌曲是否已经存在于该歌单中避免重复添加。移除歌单中的歌曲类似添加操作调用PlaylistMusicServlet?actionremove同样需要做权限校验。查询歌单详情当用户访问某个歌单页面时如playlist_detail.jsp?id1后端需要执行两次查询1查询歌单的基本信息Playlist表2查询该歌单包含的所有歌曲的详细信息关联playlist_music和music表。这里建议在Service层封装一次性查出歌单及其歌曲列表避免在JSP页面中嵌套循环查询数据库N1问题。4. 项目部署与性能优化实践4.1 使用IDEA打包与本地Tomcat部署开发完成后我们需要将项目部署到服务器。首先在IDEA中打包。Maven打包在IDEA右侧的Maven工具窗口中找到你的项目展开Lifecycle双击package。Maven会执行编译、测试并在target目录下生成一个项目名.war文件例如music-website.war。本地Tomcat测试将生成的.war文件复制到Tomcat的webapps目录下。启动Tomcat运行bin/startup.bat或startup.sh。Tomcat会自动解压war包。访问http://localhost:8080/music-website即可。重要检查确保Tomcat的conf/server.xml中配置了正确的URI编码URIEncodingUTF-8以及足够大的maxPostSize以支持文件上传。4.2 数据库连接池配置与优化直接使用DriverManager获取连接在高并发下性能极差。我们使用阿里开源的Druid连接池。在web.xml中配置Druid监控可选但推荐servlet servlet-nameDruidStatView/servlet-name servlet-classcom.alibaba.druid.support.http.StatViewServlet/servlet-class /servlet servlet-mapping servlet-nameDruidStatView/servlet-name url-pattern/druid/*/url-pattern /servlet-mapping配置后访问http://你的域名/druid可以查看详细的SQL监控、Web URI监控等对性能调优和排查慢SQL非常有帮助。数据库优化建议索引为常用的查询字段建立索引如music表的singer、uploader_idplaylist表的creator_id。SQL优化避免在循环中执行SQL尽量使用JOIN一次性查询出所需数据。对于歌单详情页使用SELECT m.* FROM music m INNER JOIN playlist_music pm ON m.id pm.music_id WHERE pm.playlist_id ?。连接池参数根据预估的访问量在Druid配置中合理设置initialSize初始连接数、maxActive最大活跃连接数、minIdle最小空闲连接数等。4.3 静态资源处理与CDN加速我们的音乐文件MP3和图片封面都是静态资源。在Tomcat中直接通过audio src/static/music/xxx.mp3访问Tomcat会从应用目录下读取文件并返回。但这会占用Tomcat的处理线程影响动态请求的并发能力。优化方案Tomcat配置静态资源缓存在conf/web.xml中找到对defaultServlet的配置增加缓存时间减少重复请求。init-param param-namecacheMaxSize/param-name param-value102400/param-value /init-param init-param param-namecacheObjectMaxSize/param-name param-value4096/param-value /init-param使用Nginx反向代理生产环境推荐将Tomcat作为应用服务器Nginx作为前端Web服务器。在Nginx配置中将/static/路径的请求直接映射到存放音乐和图片的磁盘目录并设置强缓存。这样静态资源的请求完全由高性能的Nginx处理不再经过Tomcat。server { listen 80; server_name your-domain.com; location / { proxy_pass http://localhost:8080; # 转发动态请求到Tomcat proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } location /static/ { alias /path/to/your/webapp/static/; # 直接处理静态资源 expires 30d; # 设置30天浏览器缓存 access_log off; # 可选关闭静态资源访问日志 } }考虑CDN如果用户分布广泛可以将静态资源上传到对象存储如阿里云OSS、腾讯云COS并开启CDN加速能极大提升全国乃至全球用户的访问速度。4.4 基础安全防护措施即使是一个小项目安全底线也必须守住。SQL注入防护必须使用PreparedStatement绝对不要拼接SQL字符串。这是铁律。Druid也提供了WallFilter可以一定程度上防御SQL注入。XSS防护在JSP页面上显示用户输入的内容如歌单描述、评论时使用JSTL的c:out value${content}标签它会自动进行HTML转义。或者在后端入库/出库前使用库如org.apache.commons.text.StringEscapeUtils进行转义。文件上传安全校验文件类型不能只依赖文件后缀名.mp3因为可以被伪造。更安全的方式是读取文件头Magic Number进行判断。对于MP3文件可以检查文件前几个字节。限制上传目录确保上传的文件保存在Web应用根目录之外或者即使保存在内部也要确保其不能被当作JSP/Servlet执行Tomcat默认不会执行非特定后缀的文件但安全起见最好配置。重命名文件如前所述使用不可预测的文件名UUID防止用户通过路径直接访问或覆盖其他文件。会话安全用户退出时调用session.invalidate()。考虑对管理员操作如上传、删除音乐进行二次密码验证或增加操作日志。5. 常见问题排查与调试技巧在实际开发和部署中你肯定会遇到各种问题。这里记录几个我当年踩过的坑和解决方法。5.1 中文乱码问题终极解决方案乱码是JavaWeb开发中最常见的问题之一通常出现在表单提交、数据库读写、页面显示三个环节。全局设置治本确保所有JSP页面文件本身以UTF-8编码保存IDEA中设置。在每个JSP页面头部添加% page contentTypetext/html;charsetUTF-8 languagejava pageEncodingUTF-8%。创建一个CharacterEncodingFilter并配置在web.xml中作为第一个过滤器统一设置请求和响应的编码为UTF-8。public class CharacterEncodingFilter implements Filter { public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { request.setCharacterEncoding(UTF-8); response.setCharacterEncoding(UTF-8); response.setContentType(text/html;charsetUTF-8); chain.doFilter(request, response); } }数据库连接字符串加上字符集参数jdbc:mysql://localhost:3306/music_db?useUnicodetruecharacterEncodingUTF-8useSSLfalse。确保MySQL数据库、表、字段的字符集都是utf8mb4支持所有emoji。文件上传乱码对于通过multipart/form-data上传的文件名需要在获取Part之前调用request.setCharacterEncoding(UTF-8)可能无效。更可靠的方法是在Servlet 3.0的MultipartConfig注解中暂时没有直接设置但可以在解析Part时对文件名进行手动解码如new String(fileName.getBytes(ISO-8859-1), UTF-8)但这方法已过时。最佳实践是确保前端页面表单和服务器环境编码一致并使用我们上面提供的getSubmittedFileName方法它处理了部分编码问题。5.2 音频文件无法播放或加载慢检查路径浏览器开发者工具F12的Network标签页查看对音频文件的请求是否返回404路径错误或403权限不足。确保audio src中的路径是正确且可公开访问的。检查MIME类型确保Tomcat或Nginx正确配置了MP3文件的MIME类型audio/mpeg。可以在浏览器Network中查看响应头Content-Type。加载慢如果是大文件考虑在前端实现“边下边播”流式播放。HTML5的audio标签本身支持preloadmetadata只加载元数据播放时才下载音频数据。对于超大文件服务器端可能需要支持HTTP范围请求HTTP Range RequestsTomcat默认是支持的。跨域问题如果你的音频文件存放在另一个域名下如CDN可能会遇到跨域问题。需要在存放音频文件的服务器上配置CORS跨域资源共享头部例如Access-Control-Allow-Origin: *。5.3 数据库连接耗尽或性能低下现象网站运行一段时间后打开变慢甚至出现“无法获取数据库连接”的错误。排查检查Druid监控面板如果配置了查看活跃连接数是否接近maxActive是否存在连接泄漏。连接泄漏这是最常见原因。确保在DAO层的每一个方法中获取Connection、PreparedStatement和ResultSet后无论是否发生异常都在finally块中或使用try-with-resources语法将其关闭。// 正确做法 (Java 7) public User findByUsername(String username) { String sql SELECT * FROM user WHERE username ?; try (Connection conn dataSource.getConnection(); PreparedStatement pstmt conn.prepareStatement(sql)) { pstmt.setString(1, username); try (ResultSet rs pstmt.executeQuery()) { if (rs.next()) { // ... 映射结果到User对象 return user; } } } catch (SQLException e) { // 记录日志 throw new DaoException(Query user failed, e); } return null; }慢SQL在Druid监控中找出执行时间长的SQL使用EXPLAIN命令分析其执行计划优化索引或SQL语句。连接池配置不当根据实际压力调整maxActive建议50-100起步、minIdle等参数。5.4 会话Session丢失或不一致现象用户登录后跳转到其他页面又显示未登录。可能原因及解决浏览器Cookie问题Session依赖名为JSESSIONID的Cookie。确保网站没有在跨域环境下运行并且用户浏览器没有禁用Cookie。Tomcat重启默认Session是存储在内存中的Tomcat重启会导致所有Session失效。对于生产环境可以考虑配置Session持久化到数据库或Redis中。URL重写问题如果禁用了CookieTomcat会尝试使用URL重写在URL后附加jsessionid...来维持会话。但在Servlet/JSP项目中我们通常要求启用Cookie。Filter顺序确保你的LoginFilter或其他修改Session的Filter在CharacterEncodingFilter之后否则可能出现编码问题影响Session读写。这个基于JavaWeb的小型音乐网站项目虽然技术栈不是最新的但它所涉及的用户认证、文件上传、数据库CRUD、前端交互、会话管理、部署优化等知识点是Web开发的通用基础。亲手实现一遍你对Web应用如何运作的理解会深刻得多。在实现过程中最重要的是养成好的编码习惯善用Maven管理依赖、为数据库操作编写通用的工具类如JdbcUtils、遵循MVC分层、注重异常处理和日志记录、时刻考虑安全性。这些习惯无论你将来是转向Spring Boot还是其他框架都是受益无穷的。项目完成后你还可以尝试给它增加一些新功能比如音乐搜索、歌曲评分、用户评论、基于播放历史的简单推荐或者用更现代的前端技术如Vue.js重写界面让这个“小麻雀”继续进化。本文还有配套的精品资源点击获取
返回列表