實戰(zhàn):JSP+Servlet+MySQL從部署到排錯)
簡介這是一套基于 JSP Servlet 的 Web 成績管理系統(tǒng)面向計算機相關(guān)專業(yè)學生、Java Web 課程設(shè)計人群和需要快速搭建教務(wù)管理原型的開發(fā)者。系統(tǒng)包含學生、教師、管理員三大模塊學生可查詢個人信息與本人成績教師可錄入、修改、查看和分析成績管理員可管理學生、教師、班級、課程并完成排課與用戶維護。壓縮包共 870 個文件約 39.64MB以 java、jsp 源碼為主輔以大量 js、css 與圖片資源內(nèi)含完整頁面樣式、前端交互和可導(dǎo)入的 sql 數(shù)據(jù)庫腳本xls/xlsx 兼顧成績數(shù)據(jù)導(dǎo)出場景整體目錄結(jié)構(gòu)較清晰。已有 173 人學習下載。拿到后可直接對照源碼和數(shù)據(jù)庫腳本部署理解 JSP Servlet 的分層請求處理流程也可在課程設(shè)計或期末項目中直接復(fù)用或二次開發(fā)省去從零搭建基礎(chǔ)框架的時間。1. 成績管理系統(tǒng)課程設(shè)計里繞不開的 Java Web 綜合項目成績管理系統(tǒng)是 Java Web 課程設(shè)計里出現(xiàn)頻率最高的題目之一它不像電商、論壇那么花哨但恰好把 JSP、Servlet、MySQL、Session、權(quán)限控制、增刪改查串成一條完整的線。這份編號 100010041 的項目以 JSP Servlet 為主干MySQL 存數(shù)據(jù)前端用 Bootstrap 搭界面還自帶 Summernote 富文本編輯器。系統(tǒng)按角色拆成學生、教師、管理員三個模塊學生只能查個人信息和成績教師負責成績錄入、修改、分析管理員掌握學生、教師、班級、課程的全部增刪改查外加排課和分配班級。它解決的是學校里成績數(shù)據(jù)誰錄入、誰查看、誰管理的問題權(quán)限邊界清晰、數(shù)據(jù)鏈路完整。適合做課程設(shè)計需要參考的在校生也適合想復(fù)盤完整 Java Web 流程的初級開發(fā)者。下面按架構(gòu)、部署、代碼走讀、排錯、進階五步拆開講。2. 項目骨架JSP Servlet MySQL 的請求流轉(zhuǎn)與數(shù)據(jù)表設(shè)計2.1 從請求到響應(yīng)為什么業(yè)務(wù)邏輯要放在 Servlet 而不是 JSP 里先看技術(shù)選型。JSP 負責頁面展示Servlet 負責接收請求和處理業(yè)務(wù)MySQL 負責持久化這是 Java Web 最經(jīng)典的三層組合。早期項目常有人把業(yè)務(wù)代碼直接寫進 JSP 的% %里頁面又亂又難維護這個項目的做法是讓 JSP 只做顯示Servlet 通過doGet/doPost接收前端提交的參數(shù)調(diào)用 DAO 層訪問數(shù)據(jù)庫再把結(jié)果放進 request 或 session 作用域轉(zhuǎn)發(fā)回 JSP 渲染。一個典型請求的流轉(zhuǎn)過程是瀏覽器提交表單 → Tomcat 根據(jù) web.xml 里的映射找到對應(yīng)的 Servlet → Servlet 拿到參數(shù)、校驗角色權(quán)限、調(diào)用 DAO 查詢 → 把結(jié)果 setAttribute 進 request → 用request.getRequestDispatcher(xxx.jsp).forward()跳轉(zhuǎn) → JSP 用 EL 表達式和 JSTL 把數(shù)據(jù)渲染出來。這里的 forward 是服務(wù)器內(nèi)部跳轉(zhuǎn)地址欄不變?nèi)绻龅氖切略?、修改這類寫操作需要改用 sendRedirect 重定向避免用戶刷新頁面時重復(fù)提交表單。Servlet 和 URL 的對應(yīng)關(guān)系在 web.xml 里配置像這樣servlet servlet-nameLoginServlet/servlet-name servlet-classcontroller.LoginServlet/servlet-class /servlet servlet-mapping servlet-nameLoginServlet/servlet-name url-pattern/login/url-pattern /servlet-mapping也可以直接用WebServlet(/login)注解代替項目用哪種取決于它的 Servlet 版本。web.xml 配url-pattern時要注意以/開頭寫成login這種不帶斜杠的形式啟動時不會報錯但請求永遠匹配不上全是 404。項目結(jié)構(gòu)上src 下一般是 controller 包放 Servletdao 包放數(shù)據(jù)庫訪問entity 或 bean 包放實體類web 目錄下是 JSP 頁面和靜態(tài)資源。bootstrap.min.css、style.min.css、summernote-bs3.css 這些靜態(tài)文件在資源目錄里說明界面不需要從零寫樣式重點是業(yè)務(wù)邏輯本身。2.2 六張核心表學生、教師、班級、課程、成績?nèi)绾侮P(guān)聯(lián)數(shù)據(jù)庫是這套系統(tǒng)的基礎(chǔ)。核心表有六張學生表 student、教師表 teacher、管理員表 admin、班級表 class、課程表 course、成績表 score。班級和學生是一對多課程和教師是多對一成績表通過 student_id 和 course_id 兩個外鍵把學生和課程關(guān)聯(lián)起來這是典型的學生選課成績模型。建表 SQL 的關(guān)鍵部分長這樣CREATE TABLE student ( id INT PRIMARY KEY AUTO_INCREMENT, student_no VARCHAR(20) UNIQUE NOT NULL COMMENT 學號, name VARCHAR(50) NOT NULL, password VARCHAR(64) NOT NULL, class_id INT, CONSTRAINT fk_student_class FOREIGN KEY (class_id) REFERENCES class(id) ); CREATE TABLE score ( id INT PRIMARY KEY AUTO_INCREMENT, student_id INT NOT NULL, course_id INT NOT NULL, score DECIMAL(5,2), exam_type VARCHAR(20) COMMENT 平時/期中/期末, UNIQUE KEY uk_stu_course (student_id, course_id, exam_type), CONSTRAINT fk_score_student FOREIGN KEY (student_id) REFERENCES student(id), CONSTRAINT fk_score_course FOREIGN KEY (course_id) REFERENCES course(id) );成績表里建議保留 exam_type 字段把平時、期中、期末分開存而不是只存一個最終總評。這樣教師在成績分析模塊里可以分別統(tǒng)計平時分和期末分的平均分、及格率答辯時也更有東西可講。唯一鍵 (student_id, course_id, exam_type) 能防止同一門課同一個學生重復(fù)錄入成績這是數(shù)據(jù)多錄的第一道防線。外鍵在課程設(shè)計里應(yīng)該建但要注意如果后期要批量導(dǎo)入成績外鍵約束會讓導(dǎo)入順序變得苛刻必須先導(dǎo)學生、課程再導(dǎo)成績。我一般會先正常建外鍵保證數(shù)據(jù)一致性等到要做批量導(dǎo)入時再用SET FOREIGN_KEY_CHECKS0臨時關(guān)閉檢查導(dǎo)入完再打開。這樣既不影響平時的插入操作也不會在導(dǎo)數(shù)據(jù)時翻車。2.3 三層權(quán)限Session 里的 userType 與 Filter 攔截三個模塊對應(yīng)三種身份權(quán)限控制是這套系統(tǒng)的核心。登錄成功后Servlet 會把當前用戶信息放進 session最關(guān)鍵的是一個角色標識比如 userType值可能是 1管理員、2教師、3學生。后續(xù)每個 Servlet 在處理業(yè)務(wù)前先檢查 session 里的 userType不匹配就拒絕或重定向到登錄頁。如果項目里配了 Filter權(quán)限檢查會更集中一個過濾器攔下所有請求public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { HttpServletRequest request (HttpServletRequest) req; HttpServletResponse response (HttpServletResponse) resp; HttpSession session request.getSession(false); String uri request.getRequestURI(); if (session null || session.getAttribute(userType) null) { if (uri.endsWith(login.jsp) || uri.contains(/login) || uri.contains(/static/)) { chain.doFilter(req, resp); } else { response.sendRedirect(request.getContextPath() /login.jsp); } return; } chain.doFilter(req, resp); }這段代碼的要點是白名單機制login.jsp、登錄 Servlet、靜態(tài)資源目錄放行其余請求一律檢查登錄態(tài)。request.getSession(false)加了個 false表示如果沒有 session 就返回 null 而不是新建一個避免惡意請求大量創(chuàng)建空 session 消耗內(nèi)存。靜態(tài)資源路徑不要寫死成/static/要看你項目里 CSS、JS 實際放在哪個目錄否則會出現(xiàn)頁面 HTML 正常但樣式全丟的問題。這里有個容易被忽略的點session 里建議只放 id、name、userType 這幾個必要字段不要放整個實體對象。放整個對象雖然在頁面上取值方便但 session 存在服務(wù)器內(nèi)存里并發(fā)一高內(nèi)存壓力會很明顯而且數(shù)據(jù)庫記錄更新后 session 里的舊對象不會自動同步。3. 從壓縮包到跑起來本地部署的完整步驟與參數(shù)說明3.1 環(huán)境清單JDK、Tomcat、MySQL 的版本搭配這類 JSP Servlet 項目對版本不挑剔但版本搭錯會浪費大量時間。我常用的組合是 JDK 8 Tomcat 8.5 或 9 MySQL 5.7或 8.0IDE 用 IntelliJ IDEA 社區(qū)版就夠。JDK 8 是這類老項目的舒適區(qū)Tomcat 9 對應(yīng) Servlet 4.0 規(guī)范兼容性最好如果項目里用了較老的第三方依賴降回 Tomcat 8.5 更穩(wěn)。組件推薦版本說明JDK1.8老項目最容易兼容的版本Tomcat8.5 / 9.0Servlet 容器9 對應(yīng)規(guī)范更新MySQL5.7 / 8.08.0 需要配好驅(qū)動和時區(qū)IDEIDEA / Eclipse導(dǎo)入 Web 項目的方式不同MySQL 8.0 的話JDBC 驅(qū)動類要換成com.mysql.cj.jdbc.Driver連接串里必須帶serverTimezoneAsia/Shanghai否則啟動時一訪問數(shù)據(jù)庫就報時區(qū)錯誤。這是從 5.7 升到 8.0 最常見的坑后面排查章節(jié)細說。3.2 導(dǎo)入項目IDEA 里的 Web 項目配置要點解壓壓縮包后先把目錄結(jié)構(gòu)看清楚。如果里面能直接看到 src 和 web或 WebContent目錄這是標準 Web 項目結(jié)構(gòu)。IDEA 里建議選導(dǎo)入項目選擇 Eclipse 格式導(dǎo)入而不是新建空項目再手動拖文件。打開后需要配三處Project SDK 選 1.8Language level 選 8然后在 Project Structure → Modules 里把 src 標記為 Sources把 web 目錄標記為 Web Resources指定 web.xml 的位置。配完模塊后還要在 Artifacts 里加一個 Web Exploded 或 WAR 包。IDEA 的 Create Artifact 按鈕通常會自動識別但默認的 output 目錄有時不對需要確認它指向模塊對應(yīng)的 target 或 out 目錄。這一步?jīng)Q定后面部署到 Tomcat 時編譯后的 class 和 JSP 能不能正確打包。這里我踩過一次Artifact 里沒勾選Build on make代碼改了之后 Tomcat 跑的永遠是舊的 class排查了半天才發(fā)現(xiàn)是構(gòu)建配置的問題。3.3 建庫建表SQL 腳本執(zhí)行與字符集設(shè)置項目里一般會帶一個 .sql 文件比如 score_management.sql。先用命令行或可視化工具登錄 MySQL建庫再執(zhí)行腳本mysql -u root -p CREATE DATABASE IF NOT EXISTS score_management DEFAULT CHARACTER SET utf8mb4; USE score_management; SOURCE /path/to/score_management.sql;執(zhí)行完用SHOW TABLES;確認表都建全了。注意 SOURCE 命令對路徑解析很敏感路徑里不要有中文和空格否則會報語法錯誤其實是路徑問題。數(shù)據(jù)庫字符集建議用 utf8mb4它比 utf8 多支持 emoji 和生僻字學生姓名、課程名里有特殊字符時不會變成亂碼。建完庫之后找到項目里的數(shù)據(jù)庫連接配置通常是 db.properties或者 JDBC 工具類里的常量把 url、username、password 改成你自己的。這一步很多人漏掉導(dǎo)致頁面能打開但一查數(shù)據(jù)就報連接拒絕。提示如果項目自帶的 SQL 腳本里已經(jīng)寫了 USE 語句你手動建庫后直接執(zhí)行即可如果沒寫要先切到目標庫再 SOURCE。3.4 Tomcat 部署IDEA 調(diào)試運行與 WAR 包獨立部署調(diào)試階段用 IDEA 內(nèi)置的 Tomcat 集成Run → Edit Configurations → 添加 Tomcat Server → Local選擇本地 Tomcat 安裝目錄Deployment 標簽里把 Artifact 加進去Application context 建議設(shè)為/score保持和 JSP 里的鏈接一致。啟動后訪問http://localhost:8080/score/login.jsp或項目里配置的歡迎頁。如果要做獨立部署可以把 Artifact 構(gòu)建成 WAR 包拷到 Tomcat 的 webapps 目錄下啟動 Tomcat 后它會自動解壓部署。注意上下文路徑就是 WAR 包的文件名所以包名決定了訪問路徑。改端口去 conf/server.xml 里改Connector port8080這個端口和 JDBC 連接串里的數(shù)據(jù)庫端口是兩回事別改成一樣。4. 三個角色的功能落點登錄、成績錄入與統(tǒng)計的代碼走讀4.1 登錄與角色分發(fā)一個 LoginServlet 處理三種身份登錄是所有功能的前置。項目里一般只有一個 LoginServlet接收用戶名、密碼、角色三個參數(shù)去對應(yīng)的表里查詢。核心設(shè)計是一個 session 存三種身份登錄成功后向 session 寫入 userId、userName、userType然后按 userType 重定向到不同的主頁。String userType request.getParameter(userType); String username request.getParameter(username); String password request.getParameter(password); UserDao dao new UserDao(); User user dao.login(username, password, userType); if (user null) { request.setAttribute(error, 用戶名或密碼錯誤); request.getRequestDispatcher(login.jsp).forward(request, response); return; } HttpSession session request.getSession(); session.setAttribute(userId, user.getId()); session.setAttribute(userName, user.getName()); session.setAttribute(userType, userType); response.sendRedirect(userType _index.jsp);login 方法里的 SQL 是SELECT * FROM xxx WHERE username? AND password?這里必須用 PreparedStatement 占位符不能字符串拼接否則 SQL 注入一打一個準。密碼字段如果項目是明文存儲課程設(shè)計階段可以接受但答辯時主動說一句真實項目應(yīng)該用 MD5 加鹽或 BCrypt是很自然的加分點。登錄成功用 sendRedirect 而不是 forward是為了讓地址欄變成目標主頁防止刷新時重復(fù)提交登錄表單。4.2 成績錄入與修改后端校驗和更新邊界教師模塊的成績錄入是這個項目里業(yè)務(wù)流程最完整的部分。錄入頁面的表單包含學生下拉框、課程、考試類型、分數(shù)提交到 ScoreAddServlet。分數(shù)校驗分兩層前端用 required 和數(shù)字范圍保證基本輸入后端再解析一次防止繞過前端直接 POST 非法數(shù)據(jù)。String scoreStr request.getParameter(score); if (scoreStr null || scoreStr.trim().isEmpty()) { request.setAttribute(error, 分數(shù)不能為空); request.getRequestDispatcher(score_add.jsp).forward(request, response); return; } double score Double.parseDouble(scoreStr); if (score 0 || score 100) { request.setAttribute(error, 分數(shù)必須在 0-100 之間); request.getRequestDispatcher(score_add.jsp).forward(request, response); return; } ScoreDao dao new ScoreDao(); int result dao.insertScore(studentId, courseId, examType, score);Double.parseDouble會拋 NumberFormatException如果前端有人傳了非數(shù)字字符串這里會直接 500。穩(wěn)妥做法是用 try-catch 包一下或者用正則先判斷。修改成績時SQL 更新條件要帶成績記錄的主鍵 id不能只按 studentId 和 courseId 更新否則會覆蓋同一學生同一課程不同考試類型的記錄。更新后最好把受影響行數(shù)返回給頁面提示修改成功或記錄不存在。4.3 成績統(tǒng)計分析一條 SQL 算出平均分和及格率成績分析模塊是教師功能里最能體現(xiàn) SQL 水平的環(huán)節(jié)。典型需求是查看某門課程所有學生的成績并統(tǒng)計平均分、最高分、最低分、及格率。推薦直接在 DAO 里寫聚合 SQL數(shù)據(jù)量大時不會卡頁面。SELECT c.course_name, COUNT(s.id) AS total_count, ROUND(AVG(s.score), 2) AS avg_score, MAX(s.score) AS max_score, MIN(s.score) AS min_score, ROUND(SUM(CASE WHEN s.score 60 THEN 1 ELSE 0 END) / COUNT(s.id) * 100, 2) AS pass_rate FROM score s JOIN course c ON s.course_id c.id WHERE s.course_id ? GROUP BY s.course_id;及格率用SUM(CASE WHEN ...) / COUNT計算比查出全部記錄再在 Java 里循環(huán)判斷干凈得多。ROUND 保留兩位小數(shù)避免頁面上顯示一長串浮點數(shù)。注意 GROUP BY 的字段MySQL 5.7 以上開啟了 ONLY_FULL_GROUP_BY 模式SELECT 里的非聚合列必須出現(xiàn)在 GROUP BY 中否則會直接報語法錯誤。分析結(jié)果用表格展示如果想更直觀用 Bootstrap 的進度條或純 CSS 畫幾個分數(shù)段條形圖就行不需要引入圖表庫。4.4 前端部分Bootstrap 表格與 Summernote 富文本的安全細節(jié)項目的靜態(tài)資源里有 bootstrap.min.css、style.min.css 和 summernote-bs3.css說明界面是基于 Bootstrap 3 搭建的。Bootstrap 負責表格樣式、按鈕、表單和分頁組件讓 JSP 頁面在幾乎零手寫 CSS 的情況下也像模像樣。Summernote 是輕量富文本編輯器在這類系統(tǒng)里一般用來發(fā)布公告或編輯通知它在 textarea 上初始化提交時把帶 HTML 標簽的內(nèi)容通過表單 POST 給后端。用 Summernote 要留意提交值包含完整 HTML后端如果原樣存、原樣輸出會有 XSS 風險。課程設(shè)計項目通常沒人惡意攻擊但答辯時能主動說出富文本內(nèi)容需要過濾 script 標簽會讓人明顯覺得你理解安全邊界。最簡單的過濾是在存儲前把script相關(guān)標簽替換掉或者展示時用c:out轉(zhuǎn)義。5. 排查專章JSP Servlet 項目最常見的四個翻車現(xiàn)場5.1 404 與靜態(tài)資源加載失敗Context Path 和目錄引用不一致現(xiàn)象Tomcat 啟動正常但訪問http://localhost:8080/score/login.jsp返回 404或者頁面 HTML 出來了bootstrap.min.css、summernote-bs3.css 全部加載失敗頁面光禿禿的沒有樣式。原因第一種是 Application context 配置和實際訪問路徑不一致比如 context 配成了/score但 JSP 里的鏈接寫的是/xxx/login.jsp第二種是 JSP 里引用了css/bootstrap.min.css這類相對路徑但當前頁面在子目錄下瀏覽器按相對路徑解析后找不到文件。解決統(tǒng)一約定 context 路徑IDEA 的 Deployment 標簽里配好之后不要反復(fù)改JSP 里的靜態(tài)資源引用全部用${pageContext.request.contextPath}拼絕對路徑例如link href${pageContext.request.contextPath}/static/css/bootstrap.min.css。改完配置后 Clean 一次清理 out 目錄重新構(gòu)建再重啟很多改了沒生效其實是構(gòu)建緩存。5.2 中文亂碼請求、響應(yīng)、數(shù)據(jù)庫三處都要設(shè)置 UTF-8現(xiàn)象頁面顯示中文變成問號或者往數(shù)據(jù)庫插入中文后查出來是???更隱蔽的是 GET 請求帶中文參數(shù)時亂碼。原因三層疊加。JSP 頁面沒設(shè)置% page contentTypetext/html;charsetUTF-8 %Servlet 讀 request 參數(shù)時沒設(shè)置編碼JDBC 連接串沒指定 characterEncoding或者數(shù)據(jù)庫本身字符集不是 utf8mb4。解決三處一起改。JSP 頂部統(tǒng)一加 contentType 聲明每個 Servlet 的 doPost 第一行寫request.setCharacterEncoding(UTF-8)JDBC 連接串加?useUnicodetruecharacterEncodingutf8建庫用 utf8mb4已經(jīng)建錯的用ALTER DATABASE score_management CHARACTER SET utf8mb4;修補。Tomcat 8.5 以上 GET 請求 URI 默認按 UTF-8 解碼不用額外配置老項目的 Tomcat 7 需要在 server.xml 里給 Connector 加 URIEncodingUTF-8。5.3 數(shù)據(jù)庫連接報錯驅(qū)動類、時區(qū)與密碼三座大山現(xiàn)象頁面能打開一點登錄按鈕就報Cannot create JDBC driver of class或者The server time zone value ... is unrecognized又或者Access denied for user。原因第一種是 MySQL 8.0 用了舊的com.mysql.jdbc.Driver或者驅(qū)動 jar 沒放進 WEB-INF/lib第二種是 8.0 連接串缺少 serverTimezone 參數(shù)第三種最基礎(chǔ)也最常犯db.properties 里的密碼和本機 MySQL 實際密碼不一樣。解決確認 mysql-connector-java.jar 在 WEB-INF/lib 下且版本匹配MySQL 8.0 用com.mysql.cj.jdbc.Driver連接串補serverTimezoneAsia/ShanghaiuseSSLfalse密碼逐個字符核對注意 properties 文件里密碼如果包含#或空格會被截斷需要轉(zhuǎn)義。這類純 JDBC 項目不必上連接池把每次查詢的 Connection、Statement、ResultSet 在 finally 里正確關(guān)閉才是重點不然跑幾十次就連接耗盡。5.4 Session 失效與權(quán)限繞過服務(wù)端校驗不能省現(xiàn)象長時間不操作后再點功能會跳回登錄頁這是 Session 默認 30 分鐘過期屬于正常但更嚴重的是用戶直接在地址欄輸入某個管理頁 URL不登錄也能打開。原因部分頁面只做了前端隱藏入口沒在服務(wù)端鑒權(quán)。JSP 是服務(wù)器動態(tài)渲染的直接訪問 URL 時如果請求沒經(jīng)過任何 Servlet 或 Filter 檢查Tomcat 會照常渲染輸出。Filter 如果 mapping 配的是/xxx而不是/*會漏掉一部分路徑。解決過濾器用/*攔截全部請求白名單放行登錄頁、登錄接口、靜態(tài)資源目錄參考 2.3 節(jié)代碼同時每個寫操作的 Servlet 開頭再校驗一次 userType。兩層保險看著冗余但正是這個冗余讓項目在答辯時經(jīng)得起追問。Session 過期時間在 web.xml 里配session-configsession-timeout30/session-timeout/session-config按需調(diào)整。6. 驗收與進階把課程設(shè)計變成能講清楚的作品6.1 按角色走驗收清單從建班到查成績的完整鏈路項目跑起來后建議按下面的清單過一遍而不是打開頁面看一眼就說好了。管理員登錄后先建班級、建教師、建課程、建學生再給學生分配班級換教師賬號登錄錄一門成績修改其中一條查看統(tǒng)計結(jié)果最后換學生賬號登錄確認能看到自己的成績、不能看到別人的。驗收點操作預(yù)期結(jié)果管理員建班建課新增班級、課程、教師列表出現(xiàn)新記錄分配班級把學生掛到班級下學生詳情顯示班級教師錄入成績選擇學生、課程、考試類型成績保存成功教師修改成績改一條分數(shù)更新后統(tǒng)計值變化學生查成績登錄學生賬號只看到本人成績越權(quán)訪問學生賬號直接訪問教師 URL被攔截或跳登錄頁這個清單能把三個模塊的權(quán)限邊界完整驗證一遍。特別要測的是越權(quán)路徑學生賬號直接訪問教師功能 URL 能不能進教師賬號訪問管理員頁面會不會被攔。如果都能進說明鑒權(quán)有漏洞回到 5.4 節(jié)把 Filter 補上。6.2 三個低成本改造方向連接池、操作日志、密碼加密想把項目從能跑的課程設(shè)計提升到能講的工程作品推薦三個性價比高的改造。第一把 DAO 里的裸 JDBC 換成 Druid 連接池代碼改動不大但能解釋清楚為什么不用每次新建連接第二給登錄和成績修改加日志寫一個 LogDao 把操作人、操作時間、操作內(nèi)容落表第三把明文密碼改成 MD5 加鹽存儲登錄時對輸入做同樣處理再比對。第二個改造最容易講出彩。教師修改成績時先查出修改前的值和修改后的值一起插入 log 表。答辯時這條鏈路能講五分鐘從頁面表單到 Servlet 校驗到 DAO 更新再到日志落庫一條完整的數(shù)據(jù)流。面試官對可追溯這個點的興趣遠大于對頁面樣式的興趣。6.3 演示順序與答辯技巧圍繞一條業(yè)務(wù)鏈路講故事演示項目時我習慣按登錄 → 鑒權(quán) → 一次完整業(yè)務(wù)的順序走而不是按模塊順序念功能。先打開 LoginServlet 講參數(shù)怎么接收、session 怎么存角色再帶著 Filter 講權(quán)限攔截最后選成績錄入這條鏈路把 JSP 表單 → Servlet 校驗 → DAO 插入 → 頁面回顯串起來。這樣聽的人會形成請求怎么進來、數(shù)據(jù)怎么流轉(zhuǎn)的完整認知而不是記流水賬。Java Web 項目的價值不在技術(shù)新而在于你能把一條鏈路上的每個環(huán)節(jié)講清楚。從那以后我每次拿到這類 JSP Servlet 項目都會強制自己先走一遍驗收清單再動代碼——部署路徑、權(quán)限漏洞、中文亂碼這三類坑九成出在沒做完整流程驗證。這套習慣幫我省下了大量返工時間希望也能幫到你。本文還有配套的精品資源點擊獲取