:JSP+MySQL+Tomcat畢設實戰(zhàn)項目)
簡介本資源是一份面向計算機專業(yè)本科生的Java Web畢業(yè)設計項目文檔聚焦現(xiàn)代育兒場景下的信息化服務需求為初學者提供可運行、易理解的完整系統(tǒng)實現(xiàn)方案。文檔詳細闡述了基于JSPJavaMySQL技術棧構建的育兒助手系統(tǒng)涵蓋用戶端育兒知識瀏覽、資訊獲取、論壇互動、智能問答、個人中心與管理端資訊/輪播/用戶/知識/論壇/問答等全維度后臺管理雙模塊設計邏輯與功能實現(xiàn)要點。資源為1個3.82MB的Word文檔.doc格式內(nèi)容包含開題背景、系統(tǒng)分析、數(shù)據(jù)庫設計、核心代碼說明、界面截圖及中英文摘要等典型畢設要素結構規(guī)范、排版清晰適合作為課程設計參考、答辯材料模板或二次開發(fā)基礎。目前已有200人學習下載對掌握JSP動態(tài)網(wǎng)頁開發(fā)、MySQL數(shù)據(jù)建模及Java后端業(yè)務整合具有較強實踐指導價值。1. 基于Web育兒助手系統(tǒng)一個能跑通、能部署、能答辯的Java畢業(yè)設計實戰(zhàn)項目這不是一個“概念演示”或“PPT系統(tǒng)”。它是一套真實可運行、帶完整前后端交互、含管理員后臺與用戶前臺、數(shù)據(jù)庫表結構清晰、JSP頁面邏輯閉環(huán)、連Tomcat啟動報錯都能定位到具體行號的本科級Java Web畢業(yè)設計落地包。我去年幫3個同學調試過這個項目——有人卡在MySQL中文亂碼有人死在JSP EL表達式語法錯誤還有人把web.xml里servlet-mapping路徑寫成/login.jsp而不是/login導致404刷屏。它解決的不是“有沒有”而是“能不能上線”首頁輪播圖能換、育兒知識能增刪、論壇帖子能發(fā)、智能問答能查庫返回、投訴建議能存進MySQL并被管理員看到。技術棧干凈利落JSP非Servlet裸寫有MVC雛形、JavaJDBC直連無Spring Boot遮掩適合答辯講清流程、MySQL 5.7建表含外鍵、索引、中文字段、B/S架構Chrome/Firefox直輸http://localhost:8080/baby/即進首頁。適合兩類人一是大四趕畢設 deadline 的同學需要一份不踩坑、不翻車、能本地跑通、能導出war包、能應對老師現(xiàn)場提問的實操資源二是剛學完JDBC和JSP想練手的初學者它比“學生選課系統(tǒng)”更貼近生活場景比“圖書管理系統(tǒng)”多出論壇、問答、輪播等真實模塊代碼量適中核心JSP頁28個Java類19個SQL腳本1份改起來不暈講起來有料。2. 技術選型與環(huán)境搭建為什么是JSPMySQLTomcat而不是Spring Boot2.1 JSP不是過時技術而是畢業(yè)設計的“可控性錨點”很多人看到JSP第一反應是“淘汰了”。但對本科畢設而言JSP恰恰是最可控的技術選擇。原因有三調試鏈路極短JSP修改 → 保存 → 刷新瀏覽器錯誤直接打在頁面上如org.apache.jasper.JasperException: Unable to compile class for JSP你能立刻看到哪行EL表達式寫錯、哪個JavaBean沒import而Spring Boot改個Controller要重啟、看日志、查端口沖突新手容易陷入“改了但沒生效”的玄學狀態(tài)。MVC邊界清晰本項目雖未用Struts但已自然分層——JSP負責展示% %取值、c:forEach遍歷、JavaBean封裝數(shù)據(jù)User.java,Post.java、Servlet處理請求LoginServlet.java,AddPostServlet.java、DAO操作數(shù)據(jù)庫UserDAO.java。答辯時老師問“怎么實現(xiàn)登錄驗證”你指著LoginServlet.doPost()里userDAO.findByUsernameAndPassword()這行代碼再講JDBC連接池配置邏輯扎實不空洞。教學資源豐富Eclipse Tomcat MySQL組合B站搜“JSP學生管理系統(tǒng)”有上千個帶語音的實操視頻遇到ClassNotFoundException: com.mysql.cj.jdbc.Driver你知道該去WEB-INF/lib下放mysql-connector-java-8.0.33.jar而Spring Boot的application.yml配錯新手??ㄔ赮AML縮進或spring.datasource.url里時區(qū)參數(shù)漏寫。提示本項目JSP使用JSTL 1.2c:if、c:forEach需在WEB-INF/lib中放入jstl-1.2.jar和standard-1.1.2.jar。若用Tomcat 9注意JSTL版本兼容性——Tomcat 9默認支持JSP 2.3JSTL 1.2完全可用無需升級到JSTL 1.3。2.2 MySQL 5.7選它不是因為“新”而是因為“穩(wěn)”和“好教”項目摘要里寫“MySQL數(shù)據(jù)庫”但沒指定版本。實測發(fā)現(xiàn)MySQL 5.7.44 是本項目的黃金版本。原因如下字符集開箱即用安裝時勾選utf8mb4而非舊版utf8建庫語句CREATE DATABASE baby CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;后續(xù)所有表自動繼承徹底規(guī)避“育兒知識”四個字存進數(shù)據(jù)庫變????的血淚問題。權限管理夠用不復雜畢設只需一個baby_user賬號賦予baby.*庫的SELECT,INSERT,UPDATE,DELETE權限即可。命令就一行CREATE USER baby_userlocalhost IDENTIFIED BY baby123; GRANT SELECT,INSERT,UPDATE,DELETE ON baby.* TO baby_userlocalhost; FLUSH PRIVILEGES;不像MySQL 8.0強制caching_sha2_password插件新手配JDBC URL時得加?serverTimezoneAsia/ShanghaiuseSSLfalseallowPublicKeyRetrievaltrue一長串參數(shù)極易出錯。工具鏈成熟Navicat for MySQL 15、MySQL Workbench 8.0、甚至DOS下的mysql -u root -p都能無縫連接。導入SQL腳本時Navicat右鍵“運行SQL文件”比命令行source baby.sql更直觀適合答辯前快速重裝數(shù)據(jù)庫。2.3 Tomcat 8.5畢業(yè)設計的“安全區(qū)”服務器為什么不用Tomcat 10因為Tomcat 10全面轉向Jakarta EE 9所有javax.*包名改為jakarta.*如javax.servlet.http.HttpServlet→jakarta.servlet.http.HttpServlet而本項目所有Servlet仍用javax——這是JSP規(guī)范在Java EE 8時代的標準寫法。Tomcat 8.5.94最新8.5.x版完美兼容支持JSP 2.3、Servlet 3.1、EL 3.0覆蓋項目全部語法內(nèi)存占用低Win10下啟動僅占300MB內(nèi)存老筆記本也能跑conf/server.xml里Connector port8080直接暴露端口無需反向代理本地測試零配置。部署時將項目根目錄含WEB-INF文件夾整個拖進webapps/下重命名為baby啟動Tomcat后訪問http://localhost:8080/baby/index.jsp即進首頁。若遇404先檢查webapps/baby/WEB-INF/web.xml是否存在再確認welcome-file-list里是否為welcome-fileindex.jsp/welcome-file。2.4 B/S架構不是“為了用而用”而是解決真實部署痛點本項目堅持B/SBrowser/Server而非C/SClient/Server核心動因是降低交付與演示門檻學生交畢設老師不想裝客戶端答辯現(xiàn)場換電腦只要瀏覽器就能打開管理員后臺/admin/路徑無需額外登錄態(tài)維護全靠Sessionrequest.getSession().setAttribute(admin, admin)關瀏覽器即退出安全又省事所有靜態(tài)資源CSS/JS/圖片放在webapps/baby/static/下路徑硬編碼為/baby/static/css/style.css避免相對路徑../css/style.css在不同層級JSP中失效。注意B/S意味著所有業(yè)務邏輯在服務端。例如“智能問答”模塊用戶輸入關鍵詞JSP提交到SearchServletServlet調KnowledgeDAO.searchByKeyword(keyword)查MySQL再把結果List傳回JSP用c:forEach展示——沒有前端JavaScript發(fā)AJAX沒有Vue響應式純服務端渲染。這正是答辯時老師想聽的“數(shù)據(jù)從哪來怎么查怎么傳怎么顯”3. 核心功能實現(xiàn)從注冊登錄到智能問答每一步都附可運行代碼3.1 用戶注冊與登錄Session JDBC 密碼MD5三步閉環(huán)注冊流程不是簡單存表而是包含數(shù)據(jù)校驗、密碼加密、唯一性約束三重保障。關鍵代碼在RegisterServlet.java// RegisterServlet.java protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String username request.getParameter(username).trim(); String password request.getParameter(password); String email request.getParameter(email).trim(); // 1. 前端校驗JSP中已有onsubmitreturn validateForm()此處二次校驗 if (username.length() 3 || username.length() 16) { request.setAttribute(error, 用戶名長度3-16位); request.getRequestDispatcher(register.jsp).forward(request, response); return; } if (!email.matches(^[a-zA-Z0-9._%-][a-zA-Z0-9.-]\\.[a-zA-Z]{2,}$)) { request.setAttribute(error, 郵箱格式不正確); request.getRequestDispatcher(register.jsp).forward(request, response); return; } // 2. 密碼MD5加密使用commons-codec-1.15.jar String encryptedPassword DigestUtils.md5Hex(password); // 3. 調用DAO檢查用戶名是否已存在 UserDAO userDAO new UserDAO(); if (userDAO.findByUsername(username) ! null) { request.setAttribute(error, 用戶名已被注冊); request.getRequestDispatcher(register.jsp).forward(request, response); return; } // 4. 創(chuàng)建User對象并插入 User user new User(); user.setUsername(username); user.setPassword(encryptedPassword); user.setEmail(email); user.setRegTime(new Date()); userDAO.insert(user); // 執(zhí)行INSERT INTO user VALUES(...) request.setAttribute(msg, 注冊成功請登錄); request.getRequestDispatcher(login.jsp).forward(request, response); }邏輯說明與參數(shù)說明DigestUtils.md5Hex(password)使用Apache Commons Codec庫比手寫MessageDigest簡潔且commons-codec-1.15.jar已放入WEB-INF/libuserDAO.findByUsername(username)DAO層執(zhí)行SELECT * FROM user WHERE username ?查到記錄即返回User對象否則返回nullrequest.getRequestDispatcher(xxx.jsp).forward()服務端跳轉URL不變request域中屬性如error、msg可被目標JSP獲取關鍵避坑點username.trim()防止用戶輸空格注冊email.matches(...)正則確保郵箱基礎格式數(shù)據(jù)庫user.username字段設為UNIQUE索引雙重防重。登錄邏輯在LoginServlet.java核心是比對MD5密碼// LoginServlet.java User user userDAO.findByUsernameAndPassword(username, DigestUtils.md5Hex(password)); if (user ! null) { // 登錄成功存入Session HttpSession session request.getSession(); session.setAttribute(user, user); // 后續(xù)所有頁面可通過${sessionScope.user.username}取值 response.sendRedirect(index.jsp); // 重定向避免F5刷新重復提交 } else { request.setAttribute(error, 用戶名或密碼錯誤); request.getRequestDispatcher(login.jsp).forward(request, response); }提示response.sendRedirect()是重定向URL變?yōu)?baby/index.jsprequest.getRequestDispatcher().forward()是轉發(fā)URL保持/baby/login。登錄成功必須用重定向否則用戶刷新login.jsp會再次提交表單。3.2 育兒知識管理CRUD全鏈路含分頁與分類管理員后臺的“育兒知識管理”是典型CRUD模塊但增加了按分類篩選、按發(fā)布時間倒序、分頁顯示三個實用功能。KnowledgeListServlet.java負責列表頁// KnowledgeListServlet.java protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { int page 1; int pageSize 10; String currentPage request.getParameter(page); if (currentPage ! null !currentPage.isEmpty()) { page Integer.parseInt(currentPage); } String category request.getParameter(category); // 分類篩選如新生兒 KnowledgeDAO knowledgeDAO new KnowledgeDAO(); // 1. 查詢總記錄數(shù)用于分頁計算 int totalCount knowledgeDAO.getTotalCount(category); // 2. 查詢當前頁數(shù)據(jù) ListKnowledge knowledgeList knowledgeDAO.findByPage(category, (page-1)*pageSize, pageSize); // 3. 計算總頁數(shù) int totalPage (int) Math.ceil((double) totalCount / pageSize); // 4. 將數(shù)據(jù)存入request域 request.setAttribute(knowledgeList, knowledgeList); request.setAttribute(page, page); request.setAttribute(totalPage, totalPage); request.setAttribute(totalCount, totalCount); request.setAttribute(category, category); request.getRequestDispatcher(/admin/knowledge_list.jsp).forward(request, response); }DAO層關鍵SQLKnowledgeDAO.java// 查詢總數(shù)帶分類條件 public int getTotalCount(String category) { String sql SELECT COUNT(*) FROM knowledge WHERE 11; if (category ! null !category.isEmpty()) { sql AND category ?; } try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { if (category ! null !category.isEmpty()) { ps.setString(1, category); } ResultSet rs ps.executeQuery(); if (rs.next()) { return rs.getInt(1); } } catch (SQLException e) { e.printStackTrace(); } return 0; } // 分頁查詢MySQL寫法 public ListKnowledge findByPage(String category, int offset, int limit) { String sql SELECT * FROM knowledge WHERE 11 ORDER BY create_time DESC LIMIT ?, ?; if (category ! null !category.isEmpty()) { sql SELECT * FROM knowledge WHERE category ? ORDER BY create_time DESC LIMIT ?, ?; } // ... 執(zhí)行查詢封裝為ListKnowledge }JSP分頁HTMLknowledge_list.jsp!-- 分頁導航 -- div classpagination c:if test${page 1} a hrefknowledge_list?page${page-1}${empty param.category ? : categoryparam.category}laquo; 上一頁/a /c:if c:forEach begin1 end${totalPage} vari c:choose c:when test${i page} span classcurrent${i}/span /c:when c:otherwise a hrefknowledge_list?page${i}${empty param.category ? : categoryparam.category}${i}/a /c:otherwise /c:choose /c:forEach c:if test${page totalPage} a hrefknowledge_list?page${page1}${empty param.category ? : categoryparam.category}下一頁 raquo;/a /c:if /div參數(shù)說明offset (page-1)*pageSizeMySQL分頁起點第1頁從0開始第2頁從10開始LIMIT ?, ?預編譯占位符防SQL注入${empty param.category ? : categoryparam.category}JSTL三元運算保留分類篩選參數(shù)點擊頁碼不丟失篩選條件classcurrentCSS高亮當前頁提升管理體驗。3.3 智能問答模塊不是AI而是關鍵詞模糊匹配的輕量級實現(xiàn)標題叫“智能問答”實際是基于MySQL全文索引的LIKE模糊搜索符合本科畢設定位——不吹噓大模型但功能真實可用。SearchServlet.java核心邏輯// SearchServlet.java String keyword request.getParameter(keyword).trim(); if (keyword.isEmpty()) { request.setAttribute(error, 請輸入搜索關鍵詞); request.getRequestDispatcher(search.jsp).forward(request, response); return; } // 使用MySQL全文索引需提前在knowledge表title,content字段建FULLTEXT索引 // ALTER TABLE knowledge ADD FULLTEXT(title, content); String sql SELECT * FROM knowledge WHERE MATCH(title, content) AGAINST(? IN NATURAL LANGUAGE MODE) ORDER BY MATCH(title, content) AGAINST(?) DESC; // 若未建全文索引則降級為LIKE // String sql SELECT * FROM knowledge WHERE title LIKE ? OR content LIKE ? ORDER BY create_time DESC; ListKnowledge results new ArrayList(); try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setString(1, keyword); ps.setString(2, keyword); ResultSet rs ps.executeQuery(); while (rs.next()) { Knowledge k new Knowledge(); k.setId(rs.getInt(id)); k.setTitle(rs.getString(title)); k.setContent(rs.getString(content).substring(0, Math.min(200, rs.getString(content).length())) ...); k.setCategory(rs.getString(category)); results.add(k); } } catch (SQLException e) { // 降級處理若全文索引報錯改用LIKE sql SELECT * FROM knowledge WHERE title LIKE ? OR content LIKE ? ORDER BY create_time DESC; // ... 重新執(zhí)行 } request.setAttribute(results, results); request.setAttribute(keyword, keyword); request.getRequestDispatcher(search_result.jsp).forward(request, response);建全文索引SQL執(zhí)行一次即可ALTER TABLE knowledge ADD FULLTEXT(title, content); -- 驗證是否生效SHOW INDEX FROM knowledge;優(yōu)勢與邊界MATCH...AGAINST比LIKE %keyword%快10倍以上尤其數(shù)據(jù)量超千條時IN NATURAL LANGUAGE MODE支持相關性排序搜索“發(fā)燒”時“寶寶發(fā)燒怎么辦”排在“兒童發(fā)燒物理降溫”前面不依賴外部API純數(shù)據(jù)庫能力答辯時可演示EXPLAIN SELECT ...證明性能若老師問“為什么不是真正AI”可答“本模塊聚焦信息檢索效率全文索引是MySQL原生高性能方案避免引入Python或調用第三方API帶來的部署復雜度符合B/S架構輕量級原則?!?.4 論壇發(fā)帖與回復一對多關系的JSP實現(xiàn)論壇模塊體現(xiàn)數(shù)據(jù)庫設計功底。post表主貼與reply表回復是典型一對多外鍵reply.post_id指向post.id。發(fā)帖JSPadd_post.jsp提交到AddPostServlet// AddPostServlet.java String title request.getParameter(title); String content request.getParameter(content); int userId ((User) request.getSession().getAttribute(user)).getId(); Post post new Post(); post.setTitle(title); post.setContent(content); post.setUserId(userId); post.setCreateTime(new Date()); PostDAO postDAO new PostDAO(); int postId postDAO.insert(post); // 返回自增ID // 發(fā)帖成功后重定向到該貼詳情頁 response.sendRedirect(post_detail.jsp?id postId);post_detail.jsp同時加載主貼和所有回復!-- 主貼 -- div classpost-detail h2${post.title}/h2 p classpost-meta作者${post.userName} | 發(fā)布時間${post.createTime}/p div classpost-content${post.content}/div /div !-- 回復列表 -- div classreplies h3共 ${fn:length(replies)} 條回復/h3 c:forEach items${replies} varreply div classreply-item pstrong${reply.userName}/strong${reply.content}/p p classreply-time${reply.createTime}/p /div /c:forEach /div !-- 回復表單 -- form actionAddReplyServlet methodpost input typehidden namepostId value${param.id} textarea namecontent placeholder寫下你的回復... required/textarea button typesubmit提交回復/button /formDAO層關聯(lián)查詢PostDAO.java// 查詢主貼及所有回復一次SQL避免N1 public Post findWithReplies(int postId) { String sql SELECT p.*, u.username as userName, r.id as reply_id, r.content as reply_content, r.create_time as reply_time, ru.username as reply_user_name FROM post p LEFT JOIN user u ON p.user_id u.id LEFT JOIN reply r ON p.id r.post_id LEFT JOIN user ru ON r.user_id ru.id WHERE p.id ? ORDER BY r.create_time ASC; // ... 執(zhí)行手動組裝Post對象及ListReply }注意LEFT JOIN確保無回復的帖子也能顯示ORDER BY r.create_time ASC讓回復按時間正序排列最早回復在最前JSP中${fn:length(replies)}需在頁面頂部聲明% taglib prefixfn urihttp://java.sun.com/jsp/jstl/functions %。4. 避坑指南那些讓答辯前夜崩潰的12個真實問題與解法4.1 中文亂碼從數(shù)據(jù)庫到JSP五層防線全解析現(xiàn)象MySQL里存的是“新生兒護理”網(wǎng)頁顯示“?–°?”或表單提交“張三”數(shù)據(jù)庫存成“?? ??‰”。原因亂碼是字符集傳遞鏈斷裂涉及5個環(huán)節(jié)MySQL服務器默認字符集character_set_server數(shù)據(jù)庫/表字符集CREATE DATABASE ... CHARACTER SET utf8mb4JDBC連接URL參數(shù)?characterEncodingutf8mb4Tomcatserver.xmlConnector的URIEncodingUTF-8JSP頁面編碼聲明% page contentTypetext/html;charsetUTF-8 %解決步驟1MySQL命令行執(zhí)行SHOW VARIABLES LIKE character_set_%;確保character_set_serverutf8mb4步驟2建庫時明確指定CREATE DATABASE baby CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;步驟3DBUtil.java中JDBC URL寫全private static final String URL jdbc:mysql://localhost:3306/baby?useUnicodetruecharacterEncodingutf8mb4serverTimezoneAsia/Shanghai;步驟4conf/server.xml中Connector標簽加屬性Connector port8080 protocolHTTP/1.1 URIEncodingUTF-8 ... /步驟5每個JSP頂部第一行必須是% page languagejava contentTypetext/html; charsetUTF-8 pageEncodingUTF-8%缺一不可。曾有同學只改了JSP忘了改server.xml導致GET請求URL參數(shù)仍亂碼。4.2 JSP EL表達式不解析不是語法錯是web.xml版本惹的禍現(xiàn)象JSP里寫% user.username %能顯示但c:out value${user.username}/或${user.username}直接原樣輸出字符串。原因EL表達式默認在JSP 2.0啟用但若web.xml頭聲明為Servlet 2.3舊版EL會被禁用。解決檢查webapps/baby/WEB-INF/web.xml頂部聲明!-- ? 正確Servlet 3.0EL默認開啟 -- web-app xmlnshttp://xmlns.jcp.org/xml/ns/javaee xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xsi:schemaLocationhttp://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd version3.1若看到version2.3或http://java.sun.com/dtd/web-app_2_3.dtd立即替換為上方3.1版本聲明。Tomcat 8.5完全兼容。4.3 Tomcat啟動失敗端口被占、JAR沖突、JDK版本三殺現(xiàn)象雙擊startup.bat無反應或命令行報Address already in use: JVM_Bind、java.lang.UnsupportedClassVersionError。排查三步法端口沖突netstat -ano | findstr :8080Windows或lsof -i :8080Mac/Linux找到PID后taskkill /PID PID /FJAR沖突WEB-INF/lib下若有多個mysql-connector-java-*.jar如5.1和8.0混存刪掉舊版只留mysql-connector-java-8.0.33.jarJDK版本錯配本項目編譯為Java 8javac -source 1.8 -target 1.8若系統(tǒng)裝了JDK 17Tomcat會報UnsupportedClassVersionError。解決方案下載JDK 8u202長期支持版設置JAVA_HOME指向JDK 8echo %JAVA_HOME%Windows或echo $JAVA_HOMEMac/Linux確認重啟命令行再啟Tomcat。4.4 管理員登錄404路徑大小寫與web.xml映射的隱形殺手現(xiàn)象訪問http://localhost:8080/baby/admin/提示404但/baby/index.jsp正常。原因文件路徑大小寫錯誤webapps/baby/admin/文件夾名寫成AdminWindows不敏感Linux敏感web.xml中Servlet映射路徑錯誤url-pattern/admin/login/url-pattern寫成url-pattern/Admin/Login/url-patternJSP中鏈接寫死a href/admin/index.jsp應為a href${pageContext.request.contextPath}/admin/index.jsp。解決統(tǒng)一用小寫命名所有文件夾和JSPweb.xml中url-pattern全部小寫所有JSP內(nèi)鏈接用${pageContext.request.contextPath}動態(tài)獲取上下文路徑避免硬編碼/baby。4.5 數(shù)據(jù)庫連接失敗驅動、URL、權限的鐵三角驗證現(xiàn)象java.lang.ClassNotFoundException: com.mysql.cj.jdbc.Driver或Communications link failure。驗證清單項目正確值檢查方式驅動JARmysql-connector-java-8.0.33.jar確認在WEB-INF/lib/下且無其他版本JDBC URLjdbc:mysql://localhost:3306/baby?useUnicodetruecharacterEncodingutf8mb4serverTimezoneAsia/Shanghai復制到Navicat測試能否連MySQL賬號權限baby_user對baby庫有SELECT,INSERT,UPDATE,DELETESHOW GRANTS FOR baby_userlocalhost;MySQL服務狀態(tài)正在運行services.msc中檢查“MySQL80”服務是否啟動血淚經(jīng)驗曾有同學MySQL服務名為“MySql57”JDBC URL卻寫localhost:3306實際端口是3307。務必用netstat -ano | findstr :330確認MySQL監(jiān)聽端口。5. 進階技巧讓項目從“能跑”升級為“值得講”的3個硬核操作5.1 用Log4j2替代System.out.println讓調試痕跡變成答辯加分項畢業(yè)設計答辯時老師常問“登錄失敗時你怎么知道是密碼錯還是用戶不存在” 如果回答“我看控制臺打印”顯得粗糙。換成Log4j2就能說“我配置了日志級別ERROR日志記錄異常堆棧WARN日志標記業(yè)務異常INFO日志追蹤關鍵流程所有日志按日期滾動歸檔?!辈僮鞑襟E下載log4j-api-2.20.0.jar和log4j-core-2.20.0.jar放入WEB-INF/lib/在src/下新建log4j2.xml?xml version1.0 encodingUTF-8? Configuration statusWARN Appenders RollingFile nameFileOut fileNamelogs/baby.log filePatternlogs/baby-%d{yyyy-MM-dd}-%i.log.gz PatternLayout pattern%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n/ Policies TimeBasedTriggeringPolicy / SizeBasedTriggeringPolicy size10 MB/ /Policies DefaultRolloverStrategy max30/ /RollingFile /Appenders Loggers Root levelinfo AppenderRef refFileOut/ /Root /Loggers /Configuration在LoginServlet.java中添加日志private static final Logger logger LogManager.getLogger(LoginServlet.class); // 登錄失敗時 logger.warn(登錄失敗用戶名[{}]不存在或密碼錯誤, username); // 登錄成功時 logger.info(用戶[{}]登錄成功IP{}, username, request.getRemoteAddr());效果項目根目錄生成logs/baby.log內(nèi)容如14:22:31.882 [http-nio-8080-exec-2] INFO c.b.s.LoginServlet - 用戶[zhangsan]登錄成功IP127.0.0.1答辯時打開日志文件老師一眼看到“INFO”“WARN”分級立刻感知工程規(guī)范性。5.2 為首頁輪播圖增加JSON接口暴露RESTful能力應對“你會前端嗎”靈魂拷問管理員后臺可上傳輪播圖但前臺JSP是靜態(tài)寫死img srcstatic/img/banner1.jpg。升級為動態(tài)新建BannerServlet.javadoGet()返回JSONprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { BannerDAO bannerDAO new BannerDAO(); ListBanner banners bannerDAO.findAll(); // 查詢status1的輪播圖 response.setContentType(application/json;charsetUTF-8); PrintWriter out response.getWriter(); out.print(new Gson().toJson(banners)); // 用Gson轉JSON out.flush(); }index.jsp中用JavaScript調用script fetch(${pageContext.request.contextPath}/banner) .then(response response.json()) .then(data { const carousel document.getElementById(carousel); data.forEach(banner { const item div classcarousel-itemimg src${banner.imageUrl} alt${banner.title}/div; carousel.innerHTML item; }); }); /script價值證明你理解前后端分離思想即使沒用Vue也用fetch調接口展示JSON數(shù)據(jù)交換能力為未來學Ajax打基礎接口URL/banner符合RESTful風格比getBannerList.jsp更專業(yè)。5.3 用Druid連接池替換DBUtil從“能連”到“高并發(fā)”的認知躍遷DBUtil.getConnection()每次創(chuàng)建新連接高并發(fā)時會耗盡MySQL連接數(shù)。換成Druid能監(jiān)控連接池狀態(tài)、自動回收泄漏連接、防止SQL注入。操作WEB-INF/lib/加入druid-1.2.16.jarsrc/druid.propertiesdriverClassNamecom.mysql.cj.jdbc.Driver urljdbc:mysql://localhost:3306/baby?useUnicodetruecharacterEncodingutf8mb4 usernamebaby_user passwordbaby123 initialSize5 maxActive20 minIdle5 maxWait60000 timeBetweenEvictionRunsMillis60000 minEvictableIdleTimeMillis300000 validationQuerySELECT 1 testWhileIdletrue testOnBorrowfalse testOnReturnfalse poolPreparedStatementstrue maxPoolPreparedStatementPerConnectionSize20 filtersstat,wallDBUtil.java重構為單例Druidpublic class DBUtil { private static DruidDataSource dataSource; static { Properties props new Properties(); props.load(DBUtil.class.getClassLoader().getResourceAsStream(druid.properties)); dataSource (DruidDataSource) DruidDataSourceFactory.createDataSource(props); } public static Connection getConnection() throws SQLException { return dataSource.getConnection(); } }答辯話術“我用了Druid連接池它能自動管理數(shù)據(jù)庫連接比如設置maxActive20保證最多20個并發(fā)查詢超出的請求排隊等待避免MySQL因連接數(shù)爆滿而拒絕服務。還開啟了wall防火墻過濾器能攔截SELECT * FROM user WHERE 11 OR 11本文還有配套的精品資源點擊獲取