源碼:JSP+Servlet+MySQL實戰(zhàn)解析)
簡介面向中小型團隊與高校實訓場景的 Java 考勤管理系統(tǒng)完整源碼包適合具備 Java Web 基礎、希望掌握 Spring 風格業(yè)務開發(fā)或畢業(yè)設計復用的學習者。包內(nèi)按業(yè)務模塊劃分覆蓋員工管理、考勤打卡、請假/會議申請、固定資產(chǎn)與任務調(diào)度等常見功能壓縮包共 2000 個文件約 55MB以 1336 個 Markdown 文檔、500 個 JavaScript 文件、67 個 Java 文件及 JSON/XML 配置為主文檔與配置占比高便于對照閱讀、按需檢索和二次開發(fā)。已有 170 人學習下載。其中包含控制器、服務、實體與工具類等完整分層主線功能覆蓋考勤申請、會議與員工管理。借助該源碼可快速理解考勤業(yè)務表設計、接口分層、審批流轉(zhuǎn)與前后端交互方式尤其適合課程設計、項目實訓或企業(yè)內(nèi)部門禁考勤需求的原型搭建Java、XML、SQL 等關鍵文件可直接導入 IDE 運行調(diào)試Markdown 文檔輔助梳理實現(xiàn)思路。1. 考勤管理系統(tǒng)源碼學生項目里最容易被低估的 Java 練手資源做 Java 課程設計或者畢業(yè)設計的人十有八九會撞上考勤系統(tǒng)這個題目。這份源碼就是一個典型的 Java Web 考勤管理系統(tǒng)基于 JSP Servlet MySQL 實現(xiàn)包含員工管理、部門管理、上下班打卡、考勤統(tǒng)計和月度報表導出幾個模塊。它不像商城項目那么繞但足夠把 Java Web 開發(fā)里最核心的東西串一遍數(shù)據(jù)庫設計、會話管理、請求轉(zhuǎn)發(fā)、SQL 聚合統(tǒng)計。適合兩類人一類是急著交課程設計的學生拿回去改改就能跑另一類是剛學完 Java 基礎、想找一個完整項目練手的人拆開源碼看一遍比看十篇教程都管用。2. 技術棧與模塊拆解JSP Servlet MySQL 這條老鏈路為什么還在用2.1 項目形態(tài)與選型理由先說項目形態(tài)。這份源碼是典型的 Java Web 三層結構JSP 負責頁面展示Servlet 處理請求轉(zhuǎn)發(fā)JDBC 訪問 MySQL 數(shù)據(jù)庫。沒有引入 Spring Boot也沒有用 MyBatis整條鏈路全是原生 API依賴只有 Tomcat 和 MySQL 驅(qū)動兩個 jar 包。很多讀者拿到手第一反應是這太老了。我一般會勸一句老鏈路恰恰是學習成本最低的鏈路。Spring Boot 幫你自動配置了太多東西出了問題反而不容易定位而 JSP Servlet 的每一行代碼都是顯式的請求怎么進來、怎么查庫、怎么把結果塞進 request 再轉(zhuǎn)發(fā)到頁面全程沒有黑匣子。選型理由就一條這份資源的定位是能跑、能改、能看懂不是生產(chǎn)級高并發(fā)。用原生 Servlet 的好處是依賴少只要 Tomcat 起來了就能跑不用跟 Maven 倉庫里的版本沖突較勁。常見做法是登錄校驗寫在 Filter 里業(yè)務邏輯寫在 Servlet 的 doGet/doPost 里數(shù)據(jù)庫訪問單獨抽出 DBUtil 工具類。后面章節(jié)的代碼走讀會按這個結構展開。2.2 數(shù)據(jù)庫設計三張核心表的字段與關聯(lián)考勤系統(tǒng)的數(shù)據(jù)庫設計一般圍繞三張表展開員工表、部門表、考勤記錄表。員工表掛在部門表下面考勤記錄表引用員工表的主鍵。這個資源里常見的建表 SQL 大概是這樣的CREATE DATABASE attendance DEFAULT CHARACTER SET utf8mb4; USE attendance; CREATE TABLE dept ( id INT PRIMARY KEY AUTO_INCREMENT, dept_name VARCHAR(50) NOT NULL ); CREATE TABLE employee ( id INT PRIMARY KEY AUTO_INCREMENT, dept_id INT NOT NULL, emp_no VARCHAR(20) NOT NULL UNIQUE, emp_name VARCHAR(30) NOT NULL, password VARCHAR(32) NOT NULL, role TINYINT DEFAULT 1 COMMENT 1-普通員工 0-管理員, FOREIGN KEY (dept_id) REFERENCES dept(id) ); CREATE TABLE attendance_record ( id INT PRIMARY KEY AUTO_INCREMENT, emp_id INT NOT NULL, work_date DATE NOT NULL, check_in TIME, check_out TIME, status VARCHAR(10) DEFAULT 正常, UNIQUE KEY uk_emp_date (emp_id, work_date), FOREIGN KEY (emp_id) REFERENCES employee(id) );這三張表就是整個系統(tǒng)的地基。注意兩個設計細節(jié)一是 attendance_record 里加了唯一索引 (emp_id, work_date)目的是防止同一個人同一天插入兩條打卡記錄二是 status 字段做了冗余存儲后續(xù)統(tǒng)計直接按狀態(tài)分組不用每次用上班和下班時間現(xiàn)算。如果把這三張表畫成關系圖就是典型的部門 1 對多 員工、員工 1 對多 考勤記錄。部門表是獨立的員工表通過 dept_id 關聯(lián)部門考勤記錄表通過 emp_id 關聯(lián)員工。查詢員工考勤的時候只需要一次 JOIN 就能把員工姓名和部門名稱帶上這也是后面統(tǒng)計 SQL 能寫短的前提。初始化數(shù)據(jù)也是常見的一個坑。很多版本自帶的初始化腳本只建表不插數(shù)據(jù)登錄頁沒有默認賬號。建議手動插入一條管理員數(shù)據(jù)工號 admin、密碼 admin、role 設為 0否則第一次登錄會卡在賬號不存在。2.3 角色權限管理員與普通員工的兩套菜單邏輯權限這塊原項目沒有引入權限框架就是用最簡單的方式employee 表里 role 字段區(qū)分管理員和普通員工登錄成功之后把角色值存進 Session。管理員能訪問員工管理、部門管理、考勤統(tǒng)計這些路徑普通員工只能看到自己的打卡入口和個人考勤記錄。實現(xiàn)上常見做法是寫一個權限攔截的 Filter在請求到達 Servlet 之前先檢查 Session 里的角色// AuthFilter.java 權限攔截核心邏輯 public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { HttpServletRequest request (HttpServletRequest) req; HttpSession session request.getSession(false); // 未登錄一律踢回登錄頁 if (session null || session.getAttribute(user) null) { resp.sendRedirect(request.getContextPath() /login.jsp); return; } // 已登錄但角色不匹配提示無權訪問 String role (String) session.getAttribute(role); String uri request.getRequestURI(); if (uri.contains(/admin/) !0.equals(role)) { request.setAttribute(msg, 沒有管理員權限); request.getRequestDispatcher(/error.jsp).forward(req, resp); return; } chain.doFilter(req, resp); }這段 Filter 的邏輯說明第一層判斷只解決有沒有登錄第二層判斷解決登錄了但能不能進管理頁面。如果把這兩層合并成一個 if就會出現(xiàn)普通員工直接輸入管理頁面地址也能進去的漏洞。參數(shù)說明Session 里存的是 user 對象和 role 字符串role 用 String 而不是 int是為了和頁面 JSTL 標簽里的比較寫法對齊。改成 int 也行但所有比較的地方都要同步改很容易漏。另一個容易踩的點是 request.getSession(false) 里面的 false它表示拿不到 Session 時返回 null 而不是新建一個配合后面的 null 判斷正好覆蓋未登錄場景如果誤寫成 getSession(true)匿名用戶也會被強制創(chuàng)建 Session攔截邏輯就廢了。3. 本地跑起來JDK、MySQL、Tomcat 三件套的配置順序3.1 環(huán)境版本怎么選先把環(huán)境定下來再談導入。這類老項目對版本敏感版本不匹配的翻車現(xiàn)場我見過太多給你一個不會踩雷的組合JDK 8不要拿 JDK 11 或更高跑老項目JSP 編譯和某些 JDBC 驅(qū)動在 JDK 9 之后行為有變化MySQL 5.7 或 8.08.0 需要改驅(qū)動類名和時區(qū)參數(shù)細節(jié)見 3.3Tomcat 8.5兼容性最好Tomcat 9 對 JSP 的 EL 表達式支持有輕微差異IDE 用 Eclipse 或 IntelliJ IDEA 都行關鍵是確認項目是什么工程結構順序上先裝 JDK再裝 MySQL最后解壓 Tomcat。裝完 Tomcat 先別急著往里面塞項目先啟動一次確認 8080 端口能正常訪問。這里有一個新手常犯的錯誤以為 Tomcat 一定要單獨裝。其實用 IDE 內(nèi)置的 Tomcat 插件跑更方便控制臺日志和斷點調(diào)試都更順手。版本選型的原則就一句話和項目里 lib 目錄下 jar 包的編譯版本對齊。你可以在項目里找到 mysql-connector-java 的 jar看它支持的最低 JDK 版本再決定裝哪個 JDK。JDK 和 Tomcat 都是用前先查版本很多詭異報錯都是版本不匹配引發(fā)。3.2 導入工程與初始化數(shù)據(jù)庫拿到 zip 解壓之后常見做法是把整個文件夾拷到 Tomcat 的 webapps 目錄下直接運行但我不推薦這么干。更好的方式是導入 IDE用 IDE 內(nèi)置的 Tomcat 插件跑能看到控制臺日志和斷點。初始化數(shù)據(jù)庫的步驟固定三步# 第一步登錄 MySQL mysql -uroot -p # 第二步執(zhí)行建庫建表腳本腳本文件一般在 db 或 sql 目錄下 source /your/path/attendance.sql; # 第三步驗證表是否創(chuàng)建成功 USE attendance; SHOW TABLES;執(zhí)行完這三步數(shù)據(jù)庫側(cè)就緒。注意 source 命令后面要寫文件的絕對路徑別用相對路徑否則會報 file not found。導入 IDE 的時候有一個常見的坑項目如果是 Eclipse 的 Dynamic Web Project 結構直接 File - Open 打開目錄即可如果拿到手是一個普通文件夾沒有 .project 文件要先轉(zhuǎn)成 Maven 工程或者手動勾選 Web Facet否則 IDE 不認它是 Web 項目運行按鈕是灰的。我一般會先看一眼工程根目錄下有沒有 pom.xml。有 pom.xml 就用 Maven 方式導入沒有就用純 Web 工程方式導入。兩種方式對應的啟動按鈕不一樣混淆了會在后面部署階段報各種奇怪錯誤。3.3 數(shù)據(jù)源配置jdbc.properties 逐項說明老項目的數(shù)據(jù)源配置通常寫在 src 目錄下的 jdbc.properties 或 db.properties 里。打開之后你會看到四五行配置每一行都別亂改jdbc.drivercom.mysql.jdbc.Driver jdbc.urljdbc:mysql://localhost:3306/attendance?useUnicodetruecharacterEncodingutf8 jdbc.usernameroot jdbc.password123456逐項說明jdbc.driverMySQL 5.7 用 com.mysql.jdbc.DriverMySQL 8.0 必須改成 com.mysql.cj.jdbc.Driverjdbc.urllocalhost 換成遠程數(shù)據(jù)庫 IP 就是連遠程庫characterEncodingutf8 是保中文不亂碼的關鍵參數(shù)jdbc.username / jdbc.password按本地 MySQL 的實際賬號改8.0 如果因為密碼加密方式導致連接失敗要在 url 后面追加 serverTimezoneAsia/Shanghai改完配置之后重啟 Tomcat 讓配置重新加載。驗證配置是否生效的方式很簡單隨便打開一個需要查庫的頁面如果頁面能正常顯示數(shù)據(jù)列表說明數(shù)據(jù)源通了如果報 SQLException 或 Connection refused看 5.1 的排查思路。還有一個細節(jié)jdbc.password 明文寫在 properties 文件里是課程設計項目的常態(tài)別因為這個覺得項目有問題。生產(chǎn)環(huán)境用加密或環(huán)境變量那是另一個話題。但要注意 properties 文件保存時編碼必須是 ASCII 或 UTF-8 without BOM否則中文注釋會變成亂碼甚至導致解析失敗。3.4 第一個冒煙測試從登錄頁到主界面數(shù)據(jù)庫初始化完、Tomcat 啟動成功之后先做一個冒煙測試打開瀏覽器訪問 http://localhost:8080/項目名/login.jsp用默認賬號登錄看能不能正常跳轉(zhuǎn)到主界面。如果項目名帶了中文或者空格URL 里的路徑會出問題建議把 webapps 下的項目文件夾重命名成純英文比如 attendance。冒煙測試通過之后再逐個點一遍菜單員工管理、部門管理、打卡、統(tǒng)計。這一步主要是確認每個 Servlet 的路由映射有沒有配全。很多老項目的 web.xml 里配置了一堆 servlet-mapping少配一條就會在點某個菜單時 404。我用一個笨辦法快速確認路由把 web.xml 打開把每個 url-pattern 和對應 Servlet 類名抄在一張紙上然后逐個訪問。抄一遍下來整個項目的請求流轉(zhuǎn)關系就清楚了比反復點頁面快得多。提示如果冒煙測試階段就報 404先別懷疑代碼優(yōu)先檢查訪問路徑的大小寫。JSP 文件名和 Servlet 映射都是區(qū)分大小寫的login.jsp 寫成 Login.jsp 直接 404。4. 核心代碼走讀登錄、打卡與統(tǒng)計 SQL 的三個落點4.1 登錄邏輯Session 與密碼校驗登錄是第一個值得逐行看的模塊。常見寫法是 LoginServlet 接收頁面提交的 emp_no 和 password拿著這兩個參數(shù)去查 employee 表查到就把用戶對象放進 Session。// LoginServlet.java 核心校驗邏輯 String empNo request.getParameter(empNo); String pwd request.getParameter(password); // 查詢用戶注意這里要用 PreparedStatement 而不是拼接 SQL String sql SELECT * FROM employee WHERE emp_no? AND password?; PreparedStatement ps conn.prepareStatement(sql); ps.setString(1, empNo); ps.setString(2, pwd); ResultSet rs ps.executeQuery(); if (rs.next()) { // 登錄成功user 對象和 role 同時入 Session request.getSession().setAttribute(user, empNo); request.getSession().setAttribute(role, rs.getString(role)); resp.sendRedirect(request.getContextPath() /index.jsp); } else { // 登錄失敗回到登錄頁并攜帶錯誤提示 request.setAttribute(msg, 工號或密碼錯誤); request.getRequestDispatcher(/login.jsp).forward(req, resp); }邏輯說明整個登錄成敗就取決于 rs.next() 是否為 true。這里有兩個容易忽略的細節(jié)。第一個細節(jié)是請求轉(zhuǎn)發(fā)和重定向的區(qū)別。登錄失敗用 forward 回到 login.jsprequest 里的 msg 屬性還能帶上登錄頁可以顯示工號或密碼錯誤登錄成功用 sendRedirect 跳到 index.jsp讓瀏覽器地址欄變成 index.jsp這樣刷新頁面不會重復提交表單。如果把兩者搞反會出現(xiàn)刷新一次就重復登錄一次的怪現(xiàn)象。第二個細節(jié)是密碼校驗。PreparedStatement 的 ? 占位符天然防 SQL 注入老項目里如果看到字符串拼接的查詢語句建議順手改掉。employee 表里 password 字段存的是明文課程設計階段不用糾結生產(chǎn)環(huán)境必須做加鹽哈希處理。參數(shù)說明empNo 和 password 的取值來自 request.getParameter這兩個參數(shù)名要和 login.jsp 表單里的 name 屬性完全一致差一個字母就取到 null。老項目里最常見的登錄失敗原因不是密碼錯而是參數(shù)名拼寫不一致。4.2 上下班打卡時間判定與去重打卡模塊是考勤系統(tǒng)的靈魂。常見做法是同一個 PunchServlet 處理上班和下班兩個動作通過頁面按鈕區(qū)分。時間判定邏輯是這樣的// PunchServlet.java 打卡時間判定 Timestamp now new Timestamp(System.currentTimeMillis()); java.sql.Date today new java.sql.Date(now.getTime()); // 先查今天有沒有打卡記錄 String querySql SELECT check_in, check_out FROM attendance_record WHERE emp_id? AND work_date?; PreparedStatement ps conn.prepareStatement(querySql); ps.setInt(1, empId); ps.setDate(2, today); ResultSet rs ps.executeQuery(); if (!rs.next()) { // 今天第一次打卡記為上班時間 insertSql INSERT INTO attendance_record(emp_id, work_date, check_in) VALUES(?,?,?); } else if (rs.getTime(check_in) ! null rs.getTime(check_out) null) { // 已有上班時間但沒有下班時間補下班時間 updateSql UPDATE attendance_record SET check_out? WHERE emp_id? AND work_date?; } else { // 上下班都打過了提示重復打卡 }邏輯說明這個分支結構把一天內(nèi)的打卡行為拆成三種狀態(tài)——沒打過、打了上班沒打下班、都打過了。注意 check_out 為 NULL 的判斷用 rs.getTime(check_out) null而不是 rs.getString(...).equals()因為數(shù)據(jù)庫 TIME 字段為空時是 NULL 而不是空字符串。這是新人最容易寫錯的地方寫錯了會導致已打過的下班卡被重復覆蓋。打卡去重靠的是 2.2 里那張表的唯一索引 uk_emp_date。即使代碼邏輯漏了判斷數(shù)據(jù)庫層也會攔住重復插入并拋 DuplicateKeyException。這個設計就是典型的前后端雙保險代碼判斷先攔一次數(shù)據(jù)庫唯一索引兜底。如果資源里的代碼沒有這個唯一索引建議自己加上。參數(shù)說明System.currentTimeMillis() 取的是服務器時間。如果你的應用部署在云服務器上而用戶在北京時區(qū)不一致會導致打卡時間差 8 小時解決辦法見 5.3。另外 empId 這個變量來自登錄 Session 里的用戶信息不要從前端傳過來否則任何用戶都能偽造別人的打卡記錄。4.3 月度統(tǒng)計一條 SQL 出報表考勤統(tǒng)計頁面是管理員最常用的模塊。常見做法是選一個月度然后按員工分組統(tǒng)計遲到、早退、正常天數(shù)SELECT e.emp_no AS 工號, e.emp_name AS 姓名, COUNT(*) AS 出勤天數(shù), SUM(CASE WHEN r.check_in 09:00:00 THEN 1 ELSE 0 END) AS 遲到天數(shù), SUM(CASE WHEN r.check_out 18:00:00 THEN 1 ELSE 0 END) AS 早退天數(shù) FROM attendance_record r JOIN employee e ON r.emp_id e.id WHERE DATE_FORMAT(r.work_date, %Y-%m) ? GROUP BY e.id, e.emp_no, e.emp_name;這個 SQL 的兩個要點一是 DATE_FORMAT 把日期字段格式化成 YYYY-MM 和傳入?yún)?shù)做匹配注意如果 work_date 列有索引DATE_FORMAT 會導致索引失效數(shù)據(jù)量大了之后建議改成范圍查詢 BETWEEN 2024-01-01 AND 2024-01-31二是 SUM(CASE WHEN ...) 這種寫法把遲到和早退天數(shù)一次算完避免多次掃表。參數(shù)說明遲到閾值 09:00:00 和早退閾值 18:00:00 是寫死的不同公司上下班時間不同改這兩個字符串即可但格式必須是 HH:mm:ss少一位冒號都會導致比較失效。分組這里有個細節(jié)GROUP BY 后面寫的是 e.id, e.emp_no, e.emp_name 三個字段。理論上按 e.id 分組就夠了但因為 SELECT 里查了 emp_no 和 emp_nameMySQL 在 ONLY_FULL_GROUP_BY 模式下要求這些字段都出現(xiàn)在 GROUP BY 里否則直接報錯。很多新手把 GROUP BY 縮寫成只按 e.id 分組然后在別人的機器上能跑、自己機器上報錯就是 MySQL 的 sql_mode 配置不同導致的。統(tǒng)計結果在頁面上的展示常見做法是循環(huán) ResultSet 拼 HTML 表格或者用 JSTL 標簽遍歷 List。如果資源里用的是后者你會發(fā)現(xiàn)代碼量明顯更少這也是 JSP 頁面里比 scriptlet 更推薦的寫法。5. 避坑排查數(shù)據(jù)庫連不上、亂碼、時間錯位的五個現(xiàn)場老項目跑不起來九成問題出在環(huán)境配置而不是代碼本身。我把這類考勤系統(tǒng)最常見的五個報錯現(xiàn)場整理出來每個都按現(xiàn)象、原因、解決給出處理步驟照著排查比瞎試快得多。5.1 現(xiàn)象一啟動 Tomcat 報 ClassNotFoundException現(xiàn)象第一次啟動 Tomcat 或訪問任意頁面時控制臺報 java.lang.ClassNotFoundException: com.mysql.jdbc.Driver緊接著是一大串 Caused by最后頁面顯示 500。報錯位置通常定位在 DBUtil 工具類的靜態(tài)代碼塊里 Class.forName(driver) 那一行。原因MySQL 驅(qū)動 jar 沒有放進 WebContent/WEB-INF/lib 目錄。IDE 的 build path 里加了 jar編譯能通過但 Tomcat 運行時加載的是 WEB-INF/lib 下的 jar兩邊不一致就會出現(xiàn)編譯過、運行炸的迷惑現(xiàn)象。解決分三步走。第一步確認 lib 目錄下有沒有 mysql-connector-java 的 jar沒有就下載對應版本放進去第二步右鍵項目 Clean讓 IDE 重新打包部署第三步檢查 Project Structure 里的 Artifacts 配置確認 lib 目錄被標記為 jar 目錄。多數(shù)情況做完第一步就好后兩步是防止 IDE 緩存搗亂。5.2 現(xiàn)象二頁面中文全部變成問號現(xiàn)象登錄頁面正常但員工姓名、部門名稱等中文數(shù)據(jù)顯示成 ????或者向數(shù)據(jù)庫插入中文后查出來是亂碼。有些是頁面顯示亂碼但庫里正常有些是頁面正常但庫里是亂碼兩種情況原因不同。原因三個環(huán)節(jié)至少有一個字符集沒對齊——數(shù)據(jù)庫表字符集、JDBC 連接串、JSP 頁面編碼。頁面亂碼而庫正常問題出在 JSP 編碼或 response 編碼庫亂碼則基本是 JDBC 連接串缺了 characterEncoding。解決按順序檢查先把 jdbc.url 加上 characterEncodingutf8再確認建表時用了 utf8mb4最后在 JSP 頁面頂部寫 % page contentTypetext/html; charsetUTF-8 %。三個地方統(tǒng)一成 UTF-8 之后重啟亂碼問題基本消失。還有一個冷門原因properties 文件保存時被 IDE 轉(zhuǎn)成了 GBK 編碼讀出來的連接串本身就帶亂碼這種情況把文件重新保存為 UTF-8 即可。5.3 現(xiàn)象三打卡時間比本地時間慢 8 小時現(xiàn)象用戶上午 9 點打卡數(shù)據(jù)庫里 check_in 顯示 01:00:00整個系統(tǒng)的打卡時間全部偏移 8 小時。原因服務器或 MySQL 的時區(qū)設置是 UTC而用戶在東八區(qū)。MySQL 8.0 的驅(qū)動默認讀取 serverTimezone 參數(shù)不指定就按 JVM 默認時區(qū)算兩邊不一致就出現(xiàn) 8 小時偏移。解決在 jdbc.url 后面追加 serverTimezoneAsia/Shanghai同時檢查操作系統(tǒng)時區(qū)。如果項目里取時間用的是 new Date()取到的是 JVM 時區(qū)的時間還要看 Tomcat 啟動的 JVM 參數(shù)有沒有 -Duser.timezoneAsia/Shanghai。改完配置必須重啟 Tomcat光改配置文件不重啟等于白改。5.4 現(xiàn)象四導出報表 Excel 打開是亂碼現(xiàn)象管理員導出月度考勤報表用 Excel 打開之后姓名列全是亂碼或者干脆打不開提示格式錯誤。原因很多老項目導出 Excel 用的是往 response 里直接寫 HTML 表格的偽 Excel 方案文件后綴是 .xls 但內(nèi)容其實是 HTML。如果 response 沒設置正確編碼瀏覽器按默認編碼解析中文就亂碼。解決在導出 Servlet 里顯式設置 response.setCharacterEncoding(UTF-8)并且給 Content-Type 加 charset。更穩(wěn)妥的方案是把偽 Excel 換成真正的 Excel 導出組件但課程設計階段先調(diào)編碼就夠了。注意導出的文件名如果是中文要對文件名做 URL 編碼處理否則瀏覽器端顯示文件名亂碼。5.5 現(xiàn)象五8080 端口被占用導致啟動失敗現(xiàn)象啟動 Tomcat 時控制臺報 Port 8080 required by Tomcat at localhost is already in useIDE 彈窗提示端口沖突項目根本起不來。原因要么之前啟動的 Tomcat 實例沒關閉干凈要么其他程序占用了 8080 端口。常見的是前一次異常退出IDE 里的 Tomcat 進程還掛在后臺。解決先關掉 IDE 里所有 Tomcat 實例再在命令行執(zhí)行 netstat -ano | findstr 8080 查占用進程的 PID然后 taskkill /PID 對應PID /F。如果端口是被別的服務固定占用也可以直接改 Tomcat 的 server.xml 里的 Connector port把 8080 改成 8081但要記得訪問 URL 里的端口號同步改掉。提示排查順序建議固定為端口、驅(qū)動、字符集、時區(qū)。這個順序覆蓋了考勤系統(tǒng)九成以上的啟動和運行報錯每次照著走一遍比東查一下西查一下省時間。6. 二次開發(fā)方向把考勤系統(tǒng)改造成 Spring Boot 的遷移路線6.1 從 Servlet 到 Controller先跑通再改造如果你不想停留在能跑的階段最值得做的二次開發(fā)是把它遷移到 Spring Boot。遷移的思路不是重寫而是逐層替換。先建一個 Spring Boot 工程引入 web 和 mybatis 兩個 starter然后把 LoginServlet 里的邏輯原樣搬進 ControllerPostMapping(/login) public String login(String empNo, String password, HttpSession session) { Employee emp employeeMapper.findByEmpNoAndPassword(empNo, password); if (emp ! null) { session.setAttribute(user, emp.getEmpNo()); session.setAttribute(role, emp.getRole()); return redirect:/index; } return redirect:/login?msgerror; }這里的 employeeMapper 是 MyBatis 的 Mapper 接口SQL 用注解直接寫在方法上。遷移時注意兩點一是 HttpSession 的用法不變Spring MVC 可以直接注入 HttpSession二是原來 Filter 里的權限判斷改造成 Spring 的 HandlerInterceptor邏輯幾乎一模一樣。6.2 用接口清單驗證遷移結果遷移完別急著收工。我的習慣是列一份接口清單登錄、打卡、統(tǒng)計、導出逐個對比遷移前后的輸出。從那次以后我每次拿到這類老項目都先花十分鐘把數(shù)據(jù)庫腳本和配置參數(shù)過一遍再啟動項目。先跑通、再讀碼、后改造這個順序幫我省了大量查錯時間。希望幫到你。本文還有配套的精品資源點擊獲取