多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

基于JavaWeb的小型音乐网站开发实战:从Servlet到数据库

基于JavaWeb的小型音乐网站开发实战:从Servlet到数据库 简介基于JavaWeb的小型音乐网站完整项目面向JavaWeb初学者、课程设计或毕业设计人群帮助理解在线音乐播放、下载、分类与排行等核心业务。开发环境采用Eclipse、Tomcat、MySQL与JDK内置war包可直接放入Tomcat运行测试也利于快速验证学习效果。压缩包共881个文件体积189.75MB包含222个png、116个jpg、118个gif等封面与图标素材80个jsp、60个java、122个class构成前后端核心逻辑44个jar提供依赖支持23个mp3为演示音频另有sql数据库脚本、项目配置及前端js/css文件内容覆盖完整。已有915人学习下载。资源提供可运行部署的完整工程同时附上全部源码与依赖便于二次开发音乐播放、下载、分类、排行等模块代码均可直接参考或按需挪用适合教学演示与项目实战。1. 小型音乐网站是JavaWeb项目里最该先动手的那种一个“基于JAVAWEB的小型音乐网站”说白了就是用户能注册登录、按歌手或歌名搜歌、点播放、加收藏的一套三层结构应用。它不像电商系统那样堆几十张表也不像后台管理系统那样全是增删改查页面但它把JavaWeb最核心的链路全走了一遍Servlet接收请求、Service做业务判断、DAO查MySQL、JSP渲染页面中间还夹着Session鉴权、文件路径映射、中文乱码处理这些一到生产环境就现原形的细节。想证明自己不是只会在IDEA里跑一个hello world把它完整跑通是性价比最高的一条路。适合两类人一是刚学完Servlet/JSP的学生用课程设计或者毕业设计练手二是想转Java后台开发、需要一个完整项目案例撑简历的从业者。别觉得“小型”两个字掉价小项目能把工程结构做干净、把坑填平比大项目里抄一堆没跑过的代码值钱得多。2. 技术选型为什么是Servlet/JSP而不是Spring Boot先把三层结构立起来2.1 这个规模用Servlet直写比硬套Spring Boot更合适很多人在选型第一步就纠结现在网上不都直接上Spring Boot吗为什么还用Servlet/JSP这套老古董这里要分清场景。小型音乐网站的数据量就是几千首歌、几百个用户没有分布式、没有高并发、没有微服务诉求核心价值在前端交互和业务逻辑的完整闭环上。用Servlet直写你能亲眼看到HTTP请求从Tomcat进到Servlet、再调到Service、再落到DAO的每一步出了问题能在代码里直接定位。而上Spring Boot框架帮你把容器、扫描、依赖注入全兜住了反而把Servlet运行机制遮住了对初学者来说是个黑匣子。另一个维度的考量是部署成本。Servlet/JSP打成一个WAR包丢进Tomcat的webapps目录就能跑连配置文件都不需要改。Spring Boot虽然也是内嵌Tomcat但光一个依赖下载加自动配置首次跑通的时间成本就高出不少。如果你做过对比就会发现用IDEA运行JavaWeb项目配置的复杂度差就差在这Servlet项目只要配一个Tomcat Server而Spring Boot项目还要面对Maven依赖冲突、端口占用、内嵌容器日志刷屏一堆噪音。我一般会建议用Maven管理依赖但保持在javax.servlet这个老坐标体系里Tomcat用9.x。这样既能在pom.xml里看懂依赖关系又不被Spring的自动装配干扰。下面这个依赖清单是当前项目最小可运行版本dependencies !-- Servlet APIprovided表示Tomcat已提供打WAR时不打包进去 -- dependency groupIdjavax.servlet/groupId artifactIdjavax.servlet-api/artifactId version4.0.1/version scopeprovided/scope /dependency !-- JSP支持 -- dependency groupIdjavax.servlet.jsp/groupId artifactIdjavax.servlet.jsp-api/artifactId version2.3.3/version scopeprovided/scope /dependency !-- JSTL标签库JSP页面里做循环和判断必需 -- dependency groupIdjavax.servlet/groupId artifactIdjstl/artifactId version1.2/version /dependency !-- MySQL驱动注意8.x版本驱动类名变了 -- dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId version8.0.33/version /dependency /dependency依赖里排在最前面的javax.servlet-api之所以是provided是因为Tomcat本身就带了一版Servlet实现你要是把它打进去运行时会遇到类冲突表现就是启动报java.lang.ClassNotFoundException: javax.servlet.Servlet或者LinkageError这类问题排查起来非常折磨人。JSTL则必须要带后面JSP页面里做歌曲列表的循环就靠它没有这个标签库你就只能在JSP里写Java代码页面会混得很乱。2.2 包结构按三层拆model、dao、service、servlet、filter五层各管一摊包结构这事我见过太多课程设计把所有类堆在一个包里类名只能用XxxServlet1、XxxServlet2去区分。这个问题在项目规模小的时候看不出来但当你加了搜索、分页、收藏三个功能之后类之间的依赖会变成一团乱麻。正确的做法是提前分好包每个包里只有一个职责com.example.music ├── filter // CharacterEncodingFilter等 ├── servlet // LoginServlet、SongServlet、FavoriteServlet ├── service // UserService、SongService业务逻辑层 ├── dao // UserDao、SongDao和数据访问层 ├── model // User、Song、Singer、PageResult实体类 └── util // DBUtil数据库连接工具类实体类就是纯字段加getter/setter不要在里面写业务方法。DAO层只做增删改查一个方法对应一句SQL。Service层处理业务规则比如登录时校验用户是否存在、注册时检查用户名是否重复、分页时计算总页数。Servlet层只干三件事拿参数、调Service、把结果放进request或session再转发或重定向。只要遵守这个单向依赖后面任何一个层出问题你都能在五分钟内定位到具体类。2.3 Tomcat与IDEA运行配置的关键参数很多新手在IDEA里跑这个项目卡在启动阶段不是代码问题是IDEA对JavaWeb项目的运行配置没弄明白。我常用的配置方式是Run菜单 - Edit Configurations - 新增一个Tomcat Server - Local然后指定本地Tomcat目录Deployment标签页里选Artifact为music:war explodedApplication context填/music。这里必须强调war exploded而不是war前者是解压目录模式改JSP后直接刷新浏览器就能看到变化不需要重新打包后者是压缩包模式每次改完都要重新构建开发效率低一大截。另一个关键参数在Server标签页的VM options里-Dfile.encodingUTF-8不加上这个参数Tomcat在Windows平台下默认可能用GBK读代码文件中文注释和字符串字面量全乱页面输出全是问号。这个参数不属于代码问题却比代码问题更隐蔽踩一次坑就记住了。3. 数据库设计四张表撑起音乐网站的全部业务3.1 表关系设计用户、歌手、歌曲、收藏一张关系图看明白小型音乐网站的核心不是功能多而是数据模型完整。我用四张表解决用户表user存账号信息歌手表singer存歌手名字和头像歌曲表song存歌曲元数据收藏表favorite建立用户和歌曲的关联。前两张是基础数据后一张是业务数据。为什么要把歌手单独建一张表而不是直接往歌曲表里塞一个歌手名字字段因为歌曲的歌手字段在搜索页要作为筛选条件在歌曲列表页要展示歌手名如果只存一个字符串将来想加歌手头像、歌手简介、按歌手分专辑全部要改歌曲表的结构。单独建表之后歌曲表只用存一个singer_id外键歌手信息变更时所有歌曲的展示自动同步这才是关系型数据库该有的设计姿态。收藏表则是典型的多对多关系中间表。用户可以有多个收藏歌曲一首歌也可以被多个用户收藏中间用favorite表记录(user_id, song_id)组合。如果你在用户表里加一个favorite_song_ids字段用逗号分隔一堆ID那就是给自己埋坑——查询“我收藏了哪些歌”会变成字符串切割删除一条收藏要重拼整个字段等数据库数据一多就彻底跑不动了。3.2 建表SQL字符集用utf8mb4这不是可以省的事以下是建表语句MySQL 8.0下直接执行即可CREATE TABLE user ( id INT NOT NULL AUTO_INCREMENT, username VARCHAR(50) NOT NULL COMMENT 登录名, password VARCHAR(64) NOT NULL COMMENT 推荐存SHA-256散列值, nickname VARCHAR(50) DEFAULT NULL COMMENT 展示昵称, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_username (username) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT用户表; CREATE TABLE singer ( id INT NOT NULL AUTO_INCREMENT, name VARCHAR(100) NOT NULL, avatar_url VARCHAR(255) DEFAULT NULL COMMENT 歌手头像, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT歌手表; CREATE TABLE song ( id INT NOT NULL AUTO_INCREMENT, name VARCHAR(120) NOT NULL, singer_id INT NOT NULL, album VARCHAR(120) DEFAULT NULL, duration_seconds INT DEFAULT 0 COMMENT 时长秒播放器进度条用, file_path VARCHAR(255) NOT NULL COMMENT 相对路径如 /upload/songs/xxx.mp3, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY idx_singer (singer_id), CONSTRAINT fk_song_singer FOREIGN KEY (singer_id) REFERENCES singer (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT歌曲表; CREATE TABLE favorite ( id INT NOT NULL AUTO_INCREMENT, user_id INT NOT NULL, song_id INT NOT NULL, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_user_song (user_id, song_id), KEY idx_song (song_id), CONSTRAINT fk_fav_user FOREIGN KEY (user_id) REFERENCES user (id), CONSTRAINT fk_fav_song FOREIGN KEY (song_id) REFERENCES song (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT收藏表;这里有两个设计细节值得展开。第一个是password字段长度为64对应SHA-256散列后十六进制字符串的长度不要用VARCHAR(20)存明文密码项目再小也不能明文存密码这是底线。第二个是favorite表的唯一索引uk_user_song它的作用有两个数据库层面防止同一用户重复收藏同一首歌的脏数据同时这个唯一索引还能作为查询“当前用户是否已收藏某首歌”的索引一举两得。3.3 JDBC连接不硬编码用一个DBUtil统一管理小型项目不需要上MyBatisJPA更没必要但JDBC代码要写干净。我习惯写一个DBUtil类用静态代码块加载驱动用ThreadLocal提供一个线程内的连接实例核心逻辑如下public class DBUtil { private static final String URL jdbc:mysql://localhost:3306/music_db?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai; private static final String USER root; private static final String PASSWORD your_password; static { try { Class.forName(com.mysql.cj.jdbc.Driver); } catch (ClassNotFoundException e) { throw new ExceptionInInitializerError(MySQL驱动加载失败检查lib目录); } } public static Connection getConnection() throws SQLException { return DriverManager.getConnection(URL, USER, PASSWORD); } }URL里的参数不是玄学每一项都有具体作用useUnicodetruecharacterEncodingutf8保证数据库传输时字符编码一致serverTimezoneAsia/Shanghai是MySQL 8.0连接时的必填项不写会报Server returns invalid timezone。驱动类名在MySQL 8.x里是com.mysql.cj.jdbc.Driver老版本5.x是com.mysql.jdbc.Driver类名写错启动时就立即报错这个问题下面避坑章节还会重点讲。4. 核心功能落地从登录鉴权到歌曲列表分页的完整代码链路4.1 注册登录与Session鉴权过滤器统一拦截未登录请求登录是整个网站的门户也是JavaWeb里Session机制最典型的应用场景。我写的登录Servlet如下WebServlet(/login) public class LoginServlet extends HttpServlet { private final UserService userService new UserService(); Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setCharacterEncoding(UTF-8); String username req.getParameter(username); String password req.getParameter(password); User user userService.login(username, password); if (user ! null) { HttpSession session req.getSession(); session.setAttribute(loginUser, user); session.setMaxInactiveInterval(1800); // 30分钟无操作自动过期 resp.sendRedirect(req.getContextPath() /song/list?page1); } else { req.setAttribute(error, 用户名或密码错误); req.getRequestDispatcher(/login.jsp).forward(req, resp); } } }这里有个细节登录成功用重定向登录失败用转发。重定向会改变浏览器地址栏URL用户刷新页面时不会重复提交登录表单而转发留在同一个请求里用户刷新会触发浏览器弹窗“确认重新提交表单”体验很差。这就是为什么登录成功和失败的处理方式刻意不一样的原因。setMaxInactiveInterval(1800)设置的是Session空闲超时时间单位是秒不设置的话默认取Tomcat的web.xml里的30分钟配置显式声明一次是为了让代码阅读者一眼看到这个业务规则。UserService里对应的登录逻辑是public User login(String username, String password) { String hashed DigestUtils.sha256Hex(password); return userDao.findByUsernameAndPassword(username, hashed); }密码在Service层做SHA-256散列再比较保证数据库里存的和日志里打印的都不是明文。新手容易犯的错是在Servlet里做密码散列这样业务规则散了换个注册入口还得再写一遍散列逻辑。4.2 歌曲列表与分页Servlet处理页码JSP用JSTL渲染歌曲列表是音乐站的首页重头戏要实现按歌名模糊搜索、按歌手筛选和分页。分页的做法很标准前端传page参数Servlet计算offsetSQL用LIMIT截取WebServlet(/song/list) public class SongListServlet extends HttpServlet { private final SongService songService new SongService(); Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { int page 1; int pageSize 10; String pageParam req.getParameter(page); if (pageParam ! null pageParam.matches(\\d)) { page Integer.parseInt(pageParam); } String keyword req.getParameter(keyword); PageResultSong result songService.pageQuery(keyword, page, pageSize); req.setAttribute(pageResult, result); req.getRequestDispatcher(/WEB-INF/jsp/songList.jsp).forward(req, resp); } }页码参数用正则\\d做个前置校验这是防御非法输入的第一道关。如果直接Integer.parseInt用户传一个?pageabc页面直接500错误日志还会被灌满。正则过滤后非数字自动落到默认第1页这对生产环境是非常廉价但有效的保护。DAO层对应的分页SQL如下public ListSong findPage(String keyword, int offset, int pageSize) { String sql SELECT s.id, s.name, s.album, s.duration_seconds, s.file_path, sg.name AS singer_name FROM song s LEFT JOIN singer sg ON s.singer_id sg.id WHERE s.name LIKE ? OR sg.name LIKE ? ORDER BY s.id DESC LIMIT ?, ?; try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { String like % keyword %; ps.setString(1, like); ps.setString(2, like); ps.setInt(3, offset); ps.setInt(4, pageSize); ResultSet rs ps.executeQuery(); // 循环封装Song对象 } catch (SQLException e) { throw new RuntimeException(分页查询歌曲失败, e); } }LIMIT后面的两个参数含义要记死第一个是偏移量不是页码。偏移量(页码-1)乘以每页大小。所以第2页10条数据显示的是LIMIT 10, 10而不是LIMIT 2, 10。这个计算错误在所有JavaWeb项目完整案例里都是高频翻车点写代码时用一个独立方法int offset (page - 1) * pageSize;单独计算能避免你在SQL里犯混。JSP页面用JSTL渲染列表核心结构如下% taglib prefixc urihttp://java.sun.com/jsp/jstl/core % c:forEach items${pageResult.list} varsong div classsong-item span classsong-name${song.name}/span span classsinger-name${song.singerName}/span audio controls preloadnone src${pageContext.request.contextPath}${song.filePath}/audio a href${pageContext.request.contextPath}/fav/add?songId${song.id}收藏/a /div /c:forEach注意audio标签的preload属性设成none这是小型网站很现实的一个考虑歌单页一页10首歌如果全部预加载用户一进页面就同时发起10个音频请求带宽瞬间被占满在Maven打包部署到云服务器后尤其明显。设成none表示只有点播放按钮时才加载音频数据。4.3 收藏功能与用户中心登录状态下才能操作的前端与控制层双重校验收藏功能的常见实现是用户点击收藏按钮请求到/fav/addServlet从Session里取当前登录用户再往favorite表插一条记录。这个逻辑里最容易被忽视的是权限校验的位置——前后端都要做。JSP页面上用c:if test${empty sessionScope.loginUser}控制按钮显示为“登录后可收藏”这是用户体验层面的引导Servlet里还要再做一次真实的Session校验因为用户完全可以通过URL直接访问/fav/add?songId3来绕过页面限制。WebServlet(/fav/add) public class AddFavoriteServlet extends HttpServlet { Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { HttpSession session req.getSession(); User loginUser (User) session.getAttribute(loginUser); if (loginUser null) { resp.sendRedirect(req.getContextPath() /login.jsp); return; } int songId Integer.parseInt(req.getParameter(songId)); favoriteService.add(loginUser.getId(), songId); resp.sendRedirect(req.getContextPath() /song/list); } }这个Servlet故意用doGet而不是doPost方便用户从列表页直接点链接触发。但这带来了一个隐患刷新收藏页会导致重复收藏。我们已经在favorite表上加过唯一索引uk_user_song此时重复插入会被数据库拒绝并抛异常所以Service层要捕获这个异常并吞掉——返回“收藏已存在”的提示而不是让用户看到500页面。数据库的唯一索引这时成了最后一道可靠性保障。5. 避坑JavaWeb音乐站的五个高频问题与排查路径5.1 数据库连接超时或失败现象是页面500日志里MySQL驱动报错现象项目在本机一切正常部署到服务器后第一次访问正常过一晚上再访问就报500查看日志是Connection refused或Communications link failure。原因最可能是数据库服务设置了空闲超时MySQL默认wait_timeout为8小时连接池或DriverManager维护的旧连接已被数据库关闭Java侧却毫不知情拿着死连接去执行SQL。解决小型项目如果直接用DriverManager.getConnection()每次新建连接出现这个问题的概率低一些因为每次都是全新连接。如果你用了Druid或C3P0连接池需要设置testWhileIdletrue和validationQuerySELECT 1让连接池定期探测连接活性。如果是本机IDEA连接远程数据库还要检查服务器防火墙是否放行了3306端口这个坑和代码无关但排查起来最浪费时间。5.2 中文乱码贯穿全链路从JSP页面到数据库全变问号现象歌曲名、歌手名、用户昵称在页面上显示为???或者存入数据库后查询出来是乱码。原因JavaWeb的编码链路有三个独立环节浏览器到Tomcat的请求编码、Tomcat到Servlet的解析编码、JDBC到MySQL的传输编码。任何一处不一致中文就出问题。具体来说GET请求参数编码由Tomcat的URIEncoding控制POST请求由request.setCharacterEncoding()控制响应编码由response.setCharacterEncoding()控制数据库连接由JDBC URL的characterEncoding参数控制。解决写一个Filter对所有请求统一设置编码WebFilter(/*) public class CharacterEncodingFilter implements Filter { Override 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); } }注意setCharacterEncoding必须放在getParameter()之前调用也就是Filter里最先执行。MySQL建表时统一用utf8mb4JDBC URL带上characterEncodingutf8三处对齐后中文乱码基本绝迹。个别场景下JSP文件本身要以UTF-8编码保存IDEA右侧能看文件编码别让JSP文件用GBK存着页面声明utf-8也没用。5.3 404错误WEB-INF下页面无法直接访问静态资源路径与上下文冲突现象浏览器直接访问http://localhost:8080/music/WEB-INF/jsp/songList.jsp返回404。或者CSS、JS、图片加载不出来控制台报错Failed to load resource: the server responded with a status of 404。原因把JSP放到WEB-INF目录下是安全设计Tomcat不允许用户通过URL直接访问WEB-INF内的资源只能通过RequestDispatcher.forward()由Servlet转发进入。静态资源如果写在src/main/webapp/WEB-INF里同样无法被外部访问。解决JSP页面统一放/WEB-INF/jsp/所有页面跳转方式为req.getRequestDispatcher(/WEB-INF/jsp/xxx.jsp).forward(req, resp)。CSS、JS、图片放src/main/webapp/static/目录下在JSP里引用时用${pageContext.request.contextPath}/static/css/style.css不要用相对路径css/style.css——因为转发后的浏览器地址栏URL和实际资源路径可能不在同一层相对路径会算错这一条几乎是新手必踩的我早期在这个问题上浪费过一整晚。5.4 音频文件无法播放文件路径存了绝对路径换台电脑全断现象本地播放正常把WAR包部署到另一台服务器后所有歌曲都播放不了浏览器Network面板显示音频请求403或404。原因代码里把音频文件路径存成了D:/music/xxx.mp3这样的绝对路径或者/users/admin/upload/xxx.mp3换机器后路径不存在了。更隐蔽的是如果你把音乐文件上传到Tomcat的webapps目录里Tomcat发布新版本时会把整个目录清空重建你的音乐文件就丢了。解决文件路径只存相对路径例如/upload/songs/xxx.mp3音乐文件实际存放在一个项目外的固定目录比如/data/music_upload/然后在Tomcat的conf/server.xml里配置一个虚拟目录映射Context path/upload docBase/data/music_upload reloadabletrue/这样浏览器访问/music/upload/songs/xxx.mp3时Tomcat会去/data/music_upload/songs/xxx.mp3找文件既不会丢文件也不需要把大文件打进WAR包。这是从文件上传到播放这条路线上最重要的一个工程化决策越早改越省事。5.5 Session过期导致跳楼登录后操作频繁弹回登录页现象用户听歌听到一半点收藏时突然被弹回登录页输入密码重新登录后刚才听的歌也丢了。原因默认情况下Tomcat的Session超时时间是30分钟用户长时间停留在歌曲列表页没有操作或者点击收藏时Session已过期session.getAttribute(loginUser)为null被过滤器拦截跳回登录页。解决两个方向。一个是在web.xml里把Session超时调长适用于音乐网站这种用户常挂着听歌的长时长应用session-config session-timeout120/session-timeout /session-config另一个是前端做“记住我”逻辑——用Cookie存一个加密的登录凭证Session过期后自动重新登录。小型项目做Cookie凭证容易埋安全漏洞我一般建议先调Session超时时间并在用户操作时通过Ajax心跳刷新Session等用户量大了再考虑Cookie单点登录。步子迈大了容易挭着。6. 最后一招用浏览器Network面板和一条命令把整个项目验证到位6.1 全链路验证逐个接口检查HTTP状态码项目部署后不要急着点页面看效果先用接口视角把每一层确认一遍。我习惯用curl来验证后台接口是否正常回答注意这只是本地验证工具不是线上压测别拿它做高并发测试# 验证首页可访问-o丢弃响应体-w输出状态码 curl -s -o /dev/null -w %{http_code} http://localhost:8080/music/ # 验证登录接口是否接受POST curl -s -o /dev/null -w %{http_code} -X POST -d usernameadminpassword123456 http://localhost:8080/music/login # 验证静态资源是否可访问 curl -s -o /dev/null -w %{http_code} http://localhost:8080/music/static/css/style.css三个状态码如果是200、302或200、200说明应用上下文、Servlet路由和静态资源映射都通了。如果首页返回404先看Application context是不是写成了/music如果登录接口返回500打开Tomcat的logs/catalina.out文件搜索关键字Exception错误堆栈前十行通常就能定位问题。浏览器端验证用F12的Network面板过滤Audio请求点击播放按钮时应当能看到一个206 Partial Content响应这是服务器支持音频分段传输的正确表现。如果看到的是200并且整个文件被一次性拉下来说明Tomcat的静态资源处理器没有正确识别音频文件要在web.xml里补上mime-mappingmime-mapping extensionmp3/extension mime-typeaudio/mpeg/mime-type /mime-mapping6.2 调试技巧监听catalina.out与前端报错一起交叉定位如果接口状态码正常但页面点击报错这时要前后端配合排查。先看浏览器Console里的报错是JavaScript语法错误还是请求状态失败再到logs/catalina.out里搜同一个时间点的后端日志。我有个笨但有效的习惯在Servlet的doGet和doPost第一行先写一行log.info(进入SongListServlet);一旦页面白屏先确认后端有没有收到请求。有日志说明Servlet路由没问题问题在JSP渲染没日志说明请求根本没到后端问题在过滤器或静态资源映射上。这个日志法比我见过的大部分调试技巧都有效因为JavaWeb的请求链路太长了先把入口确认了再往下追不迷茫。最后一章还得提醒你一件事Session相关的调试别只看后端。浏览器F12的Application标签页里能看到当前站点的Cookie验证Session失效问题时先看看浏览器里JSESSIONID这个Cookie在不在、过期时间是多久往往能省掉半小时瞎猜的时间。希望这篇能把这条路给你趟平一部分剩下的就是你自己动手把代码敲一遍踩过的那几个坑比你背十遍笔记都管用。希望帮到你。本文还有配套的精品资源点击获取
返回列表