源碼解析與復(fù)現(xiàn)指南)
簡介一套基于 jspjsservletjdbcmysql 原生界面開發(fā)的后臺管理系統(tǒng)源碼適合 Java Web 初學(xué)者、課程設(shè)計或畢業(yè)設(shè)計參考。項目不依賴復(fù)雜前端框架完整實現(xiàn)登錄、注冊、用戶/產(chǎn)品增刪改查、分頁查詢、圖片上傳、退出及未登錄過濾器攔截等后臺常見功能可幫助讀者理解 servletjdbc 的請求處理與數(shù)據(jù)庫交互流程。壓縮包共92個文件含14個java與12個jsp源碼、28個class編譯文件以及jar依賴、xml配置、數(shù)據(jù)庫腳本和docx導(dǎo)入步驟說明整體僅3.04MB便于快速下載與本地部署。附帶的 mydb.sql 可直接建庫工程導(dǎo)入步驟與運行必讀文檔能減少環(huán)境配置彎路。已有3371人學(xué)習(xí)下載適合需要完整可運行后臺項目或希望逐模塊拆解原生 Java Web 開發(fā)的讀者。1. 這套 jspservletjdbcmysql 后臺管理系統(tǒng)老技術(shù)棧為什么還能救急去年我?guī)У囊粋€練手小組把課程設(shè)計題目定為“產(chǎn)品后臺管理系統(tǒng)”要求不許用 Spring Boot必須用 Java 原生的 jspservletjdbcmysql 把界面和邏輯寫出來。折騰兩周后他們交上來的東西和這份 xianfengProject 的結(jié)構(gòu)幾乎一樣登錄、注冊、產(chǎn)品增刪改查、分頁、圖片上傳、過濾器攔 session。我當(dāng)時把這套源碼當(dāng)參考答案發(fā)給組里新人時說了一句話把這幾條鏈路跑通再去碰框架腦子里才有“請求到底是怎么穿過后端”的那張地圖?,F(xiàn)在很多培訓(xùn)項目上來就是 Spring BootMyBatis-Plus模板一生成servlet 和 jdbc 反而成了黑匣子。這套源碼值錢的地方恰恰是它把黑匣子拆開給你看分頁用 limit 怎么算、圖片上傳怎么落盤、過濾器怎么攔未登錄用戶。適合三類人剛學(xué)完 JavaWeb 想找完整閉環(huán)案例的在校生準備 java 面試想復(fù)習(xí) servlet 和 jdbc 底層細節(jié)的求職者以及需要一套老技術(shù)棧管理系統(tǒng)改改就交差的在職工程師。我照著跑通一次全程沒遇到玄學(xué)問題但確實有幾個坑后面單開一章說。2. 項目功能與原理解剖登錄、產(chǎn)品管理、過濾器是怎么串起來的2.1 五個功能模塊的請求映射關(guān)系這套系統(tǒng)的主入口是 index.jsp上面放了登錄和注冊兩個入口。登錄成功之后 session 里會寫入用戶標識然后跳轉(zhuǎn)到產(chǎn)品管理總頁面。產(chǎn)品管理頁承載了最重的業(yè)務(wù)查詢某個產(chǎn)品、添加、修改、批量刪除、查看詳情、分頁展示。退出功能就是清 session 回主頁邏輯最簡單但承載著整個權(quán)限閉環(huán)。從請求路徑來看整個系統(tǒng)的流轉(zhuǎn)關(guān)系大概是這樣一張映射表請求路徑對應(yīng)處理說明/index.jsp主頁展示未登錄時的默認落地頁/login.doLoginServlet校驗用戶名密碼成功則寫入 session/register.doRegisterServlet新增用戶寫入 t_user 表/product/list.doProductServlet(list)分頁查詢產(chǎn)品默認 pageIndex1/product/add.doProductServlet(add)添加產(chǎn)品涉及圖片上傳/product/update.doProductServlet(update)修改產(chǎn)品/product/batchDelete.doProductServlet(batchDelete)批量刪除/logout.doLogoutServlet清除 session 后 redirect 到 index.jsp我在實際復(fù)現(xiàn)時會先畫這張表再去對照源碼里的 servlet 映射。因為原生 servlet 項目沒有框架里那種注解路由或者只在 servlet 上用 WebServlet通讀一遍 web.xml 或注解整個功能邊界就清楚了。2.2 目錄結(jié)構(gòu)里藏著的老工程痕跡壓縮包解開后的內(nèi)容很有意思它同時保留了 .idea 和 .mymetadata、.project、.classpath說明這份源碼在 IntelliJ IDEA 和 MyEclipse 里都被打開過。xianfengProject.iml 是 IDEA 的模塊文件out 目錄是 IDEA 編譯輸出目錄WebRoot 是 web 根目錄。這些細節(jié)對復(fù)現(xiàn)有用用 IDEA 導(dǎo)入時直接 Open 這個目錄IDEA 會讀取 .iml 恢復(fù)模塊結(jié)構(gòu)用 Eclipse/MyEclipse 導(dǎo)入時則看 .project 和 .classpath。src 目錄放 Java 源碼servlet、dao、bean、utilWebRoot 目錄放 jsp 頁面、WEB-INF/web.xml、js、css、上傳圖片目錄壓縮包內(nèi)附帶的 mydb.sql 是初始化腳本工程導(dǎo)入步驟.docx 和運行必讀.txt 是作者寫的操作說明我一般建議先打開運行必讀.txt 再動手因為這種老工程最容易翻車的就是環(huán)境版本不匹配。正文里寫了 jdk1.7、tomcat7、mysql5.5/5.6/5.7這三個版本必須對齊別腦子一熱裝個 tomcat10servlet 包名都變了代碼直接編譯不過。2.3 mydb.sql 里的核心表結(jié)構(gòu)設(shè)計這套系統(tǒng)只需要兩張核心表用戶表 t_user 和產(chǎn)品表 t_product。產(chǎn)品表要存圖片路徑所以單獨留了一個 image_path 字段價格用 DECIMAL 而不是 FLOAT避免精度問題。下面是按常規(guī)設(shè)計補全的建表 SQL和源碼里 mydb.sql 的思路一致CREATE DATABASE IF NOT EXISTS xianfeng_db DEFAULT CHARSET utf8mb4; USE xianfeng_db; CREATE TABLE t_user ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(50) NOT NULL, created_at DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE t_product ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(100) NOT NULL, price DECIMAL(10,2) NOT NULL DEFAULT 0.00, description TEXT, image_path VARCHAR(255), created_at DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; INSERT INTO t_user(username, password) VALUES(admin, 123456);這里有兩個細節(jié)值得注意。第一字符集要 utf8mb4 而不是 utf8否則插入 emoji 或生僻字時 jdbc 連接 mysql 會直接報 Incorrect string value。第二密碼字段設(shè)計成 VARCHAR(50) 是課程設(shè)計常見做法生產(chǎn)環(huán)境絕不這么干至少得加鹽哈希。我復(fù)現(xiàn)時只是為了跑通流程所以沿用原設(shè)計的明文存儲但如果你要拿這套資源改造成真正的工程密碼存儲是第一件必須改的事。3. 從壓縮包到跑通環(huán)境、導(dǎo)入、建庫、部署四步走3.1 環(huán)境版本對齊少一步后期就是玄學(xué)現(xiàn)場老工程最怕的就是版本漂移。項目說明明確寫了 jdk1.7、tomcat7、mysql5.5/5.6/5.7。我本地用的組合是 jdk1.7 tomcat7.0.94 mysql5.7.44一次跑通。如果你電腦上已經(jīng)裝了高版本 JDK比如 JDK8 或 JDK11編譯目標可以調(diào)成 1.7 來兼容但 tomcat 建議老老實實用 7.x。tomcat8.5 以上對老式 jsp 的隱式對象處理有細微差異雖說大概率沒問題但沒必要為了一個復(fù)現(xiàn)去賭兼容性。還有 jdbc 驅(qū)動版本mysql5.7 用 mysql-connector-java-5.1.49.jar 最常見驅(qū)動類名是 com.mysql.jdbc.Driver。如果你手頭是 8.x 的驅(qū)動包驅(qū)動類名要換成 com.mysql.cj.jdbc.Driver連接串還要額外加 serverTimezone 參數(shù)。這一步是 jdbc 連接 mysql 的經(jīng)典坑后面避坑章細說。3.2 用 IDEA 導(dǎo)入工程源碼自帶 .idea 目錄和 iml 文件所以用 IntelliJ IDEA 打開最省事。不是新建工程而是 File - Open 直接選中 xianfengProject 目錄IDEA 會根據(jù) iml 恢復(fù)模塊配置。打開后第一件事是檢查 Project StructureProject SDK: 選 1.7 或按需調(diào)成 1.8編譯級別 Level 7 Project language level: 7 - Diamonds, ARM, multi-catch 等 Modules - Dependencies: 確認 lib 下的 mysql-connector 等 jar 已加入之后配置 TomcatRun - Edit Configurations - 左上角 - Tomcat Server - Local在 Deployment 頁簽里添加 ArtifactApplication context 填 /xianfengProject。這個 context path 決定了你訪問地址是 http://localhost:8080/xianfengProject/index.jsp。邏輯說明老式 web 工程在 IDEA 里的部署方式是 Exploded解壓目錄不是 war 包。這樣做的好處是 jsp 修改后刷新頁面就能看到不用重新打包。你在 Deployment 里看到的 artifact 類型應(yīng)該是 Web Application: Exploded。3.3 初始化數(shù)據(jù)庫與賬號配置打開命令行進入 mysql 的 bin 目錄執(zhí)行mysql -u root -p mydb.sqlmydb.sql 在壓縮包根目錄建議先用記事本打開看一眼確認里面的庫名和表名。如果腳本里沒有 CREATE DATABASE你需要手動先建庫再使用。導(dǎo)入成功后在 mysql 命令行執(zhí)行 SHOW TABLES; 能看到 t_user 和 t_product 就說明數(shù)據(jù)庫這邊齊活了。然后打開源碼里的 jdbc 配置文件一般是 src 下名為 db.properties 或 jdbc.properties 的文件改成你本地的賬號密碼jdbc.drivercom.mysql.jdbc.Driver jdbc.urljdbc:mysql://localhost:3306/xianfeng_db?useUnicodetruecharacterEncodingutf8 jdbc.usernameroot jdbc.password你的數(shù)據(jù)庫密碼參數(shù)說明useUnicodetrue 和 characterEncodingutf8 這兩個參數(shù)建議保留否則從 mysql 讀中文再寫進 jsp 顯示時容易出現(xiàn)亂碼。端口 3306 如果你本地改過也要同步換。這條連接串是 jdbc 連接 mysql 最經(jīng)典的寫法很多框架的底層數(shù)據(jù)源本質(zhì)上也在拼這么一串。3.4 啟動 Tomcat 并跑通第一個用例啟動前先確認四件事tomcat 端口沒被占、jdk 版本匹配、artifact 已經(jīng)部署、mysql 服務(wù)是啟動狀態(tài)。然后點 IDEA 里的 Run 啟動 Tomcat控制臺出現(xiàn) Server startup 日志后瀏覽器訪問http://localhost:8080/xianfengProject/index.jsp首次訪問能看到主頁說明 web 應(yīng)用沒問題。然后用 admin/123456 登錄如果能進產(chǎn)品管理頁這就算跑通了。接下來建議立刻做兩件事驗證功能完整性隨便添加一個帶圖片的產(chǎn)品然后在前臺翻幾頁看分頁是否正常。第一輪復(fù)現(xiàn)只要這兩條鏈路通了這套源碼的核心功能就全部驗證到位了。4. 源碼里值得一行行讀的三個實現(xiàn)分頁、圖片上傳、過濾器4.1 分頁查詢limit 參數(shù)與頁腳邏輯原生 jdbc 做分頁思路比 MyBatis-Plus 的 IPage 要直接得多。核心就是 SQL 拼 limit前端把當(dāng)前頁碼傳給 servletservlet 通過 request.getParameter 拿參數(shù)dao 層計算偏移量后執(zhí)行查詢。下面是 ProductDao 分頁方法的核心代碼public ListProduct findByPage(int pageIndex, int pageSize) { ListProduct list new ArrayListProduct(); String sql SELECT id, name, price, description, image_path FROM t_product ORDER BY id DESC LIMIT ?, ?; int start (pageIndex - 1) * pageSize; try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setInt(1, start); ps.setInt(2, pageSize); try (ResultSet rs ps.executeQuery()) { while (rs.next()) { Product p new Product(); p.setId(rs.getInt(id)); p.setName(rs.getString(name)); p.setPrice(rs.getBigDecimal(price)); p.setDescription(rs.getString(description)); p.setImagePath(rs.getString(image_path)); list.add(p); } } } catch (SQLException e) { e.printStackTrace(); } return list; }代碼邏輯start 計算的是跳過的記錄數(shù)pageIndex 從 1 開始第一頁就是 limit 0, 10。ORDER BY id DESC 表示新品在前后來我在改造時加了“默認按添加時間倒序”的需求邏輯同理。參數(shù)說明pageSize 是每頁行數(shù)一般在 servlet 里寫死成常量或者從前端下拉框傳入這里用 PreparedStatement 預(yù)編譯也順便防了 SQL 注入是原生 jdbc 寫法的標準姿勢。配套的還要有查詢總記錄數(shù)的 count 方法頁腳才能顯示共 X 頁public int count() { String sql SELECT COUNT(*) FROM t_product; try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql); ResultSet rs ps.executeQuery()) { if (rs.next()) { return rs.getInt(1); } } catch (SQLException e) { e.printStackTrace(); } return 0; }前端頁腳計算總頁數(shù)時用 (total pageSize - 1) / pageSize 代替除法向上取整避免浮點誤差。這是原生分頁里最后一步很多新手在這里翻車。4.2 圖片上傳servlet3.0 的 Part 接口圖片上傳是整套系統(tǒng)里最有技術(shù)含量的環(huán)節(jié)。摘要里寫了圖片上傳功能實現(xiàn)上在 tomcat7 環(huán)境可以直接用 servlet3.0 的 Part 接口不需要 commons-fileupload。product 表單里 input 標簽要加 enctypemultipart/form-data這是第一個必須注意的點。WebServlet(/product/add.do) public class ProductAddServlet extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding(utf-8); String name request.getParameter(name); Part part request.getPart(image); String fileName getFileName(part); String uploadPath getServletContext().getRealPath(/) upload; File dir new File(uploadPath); if (!dir.exists()) { dir.mkdirs(); } part.write(uploadPath File.separator fileName); String price request.getParameter(price); String desc request.getParameter(description); // 組裝 Product 對象調(diào)用 dao 層插入 t_product // image_path 字段存 upload/文件名方便頁面拼接展示 response.sendRedirect(list.do?pageIndex1); } private String getFileName(Part part) { String header part.getHeader(content-disposition); String[] arr header.split(;); for (String s : arr) { if (s.trim().startsWith(filename)) { return s.substring(s.indexOf() 2, s.length() - 1); } } return default.png; } }這段代碼有幾個關(guān)鍵細節(jié)。getRealPath(/) 返回的是 webapp 部署后的磁盤絕對路徑upload 目錄要在第一次上傳前創(chuàng)建否則 part.write 會報 FileNotFoundException。getFileName 這個方法不能省Part 對象沒有直接提供原文件名方法只能從 content-disposition 頭里截取。圖片存儲路徑建議存相對路徑 upload/文件名頁面 標簽直接寫 srcupload/xxx.png 就能展示這也是摘要里提到“jsp 圖片如何定位”的常見解法。4.3 過濾器session 驗證與放行路徑過濾器是這套系統(tǒng)安全機制的核心。如果用戶沒登錄直接訪問產(chǎn)品管理頁面必須被攔回 index.jsp。實現(xiàn)就是實現(xiàn) javax.servlet.Filter 接口在 doFilter 里檢查 sessionWebFilter(/*) public class LoginFilter implements Filter { public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { HttpServletRequest request (HttpServletRequest) req; HttpServletResponse response (HttpServletResponse) resp; String uri request.getRequestURI(); String ctx request.getContextPath(); String path uri.substring(ctx.length()); boolean isStatic path.startsWith(/js/) || path.startsWith(/css/) || path.startsWith(/upload/) || path.endsWith(.js) || path.endsWith(.css); boolean isPublic path.equals(/index.jsp) || path.equals(/login.do) || path.equals(/register.do) || path.equals(/logout.do); HttpSession session request.getSession(false); boolean loggedIn session ! null session.getAttribute(user) ! null; if (loggedIn || isStatic || isPublic) { chain.doFilter(req, resp); } else { response.sendRedirect(ctx /index.jsp); } } }這個過濾器的邏輯要仔細理解放行三類請求——已登錄用戶的請求、靜態(tài)資源、公開頁面。沒有登錄且不滿足放行條件的重定向回 index.jsp。我見過很多新手寫的過濾器只判斷了 session結(jié)果把登錄頁面和靜態(tài) JS 全都攔了頁面樣式全丟或者死循環(huán)重定向。注意 getSession(false) 不要寫成 getSession()帶參 false 不會自動創(chuàng)建 session避免未登錄用戶每次訪問都被強制生成 session這是個資源浪費的隱形坑。5. 避坑指南復(fù)現(xiàn)這套源碼最容易翻車的五個位置5.1 環(huán)境部署與啟動過程中的典型問題現(xiàn)象一執(zhí)行 net start mysql 時提示服務(wù)名無效或者服務(wù)正在啟動后立刻停止。我遇到這臺機器裝的是 mysql5.7.44之前用安裝版裝過一次后來又解壓了一份免安裝版注冊表里的服務(wù)名根本沒對上。原因服務(wù)名不是默認的 mysql而是你安裝時指定的名稱可能是 MySQL57 或自定義的名字。解決用管理員身份打開 CMD執(zhí)行 sc query 或直接到服務(wù)管理器里看服務(wù)名再執(zhí)行 net start 服務(wù)名如果服務(wù)列表里沒有就用 mysqld --install 先注冊服務(wù)再啟動?,F(xiàn)象二本地裝了 JDK8但源碼在編譯時報錯看不到 API 或語法級別不對。這套工程注釋里寫的是 jdk1.7如果直接用 IDEA 默認 SDK 打開編譯級別可能帶著 1.8 語法servlet 代碼沒問題但某些老 lib 會和 JDK8 產(chǎn)生 bytecode 版本沖突。解決Project Structure 里把 Project SDK 定義為 1.7沒有就先裝一個Language level 選 7Modules 里的 SDK 也同步改。如果你機器上只有 JDK8 不想再裝舊的把編譯 target 調(diào)成 1.7 也能緩解大部分問題。現(xiàn)象三Tomcat 啟動時端口被占用報 SEVERE 級別的 Socket bind 失敗。原因多半是上次 IDEA 非正常關(guān)閉java.exe 進程還占著 8080。解決CMD 執(zhí)行 netstat -ano | findstr :8080 查到 PID再 taskkill /PID 你的PID /F殺完重啟就好。血淚經(jīng)驗不要一上來就把 tomcat 的 server.xml 端口改成 8081治標不治本而且 session 里的回跳地址不會自動適配新端口?,F(xiàn)象四頁面訪問 404顯示源服務(wù)器未能找到目標資源的表示。原因一般是 artifact 沒部署成功或者 context path 不是 /xianfengProject。Tomcat 的部署機制里war 或 exploded artifact 要掛在指定 context 下訪問路徑是 ip:port/context/資源路徑少一層或多一層都會 404。解決檢查 Run Configuration 的 Deployment 頁簽確認 artifact 已經(jīng)添加且 Application context 寫的 /xianfengProject然后重啟 Tomcat 重新部署。現(xiàn)象五數(shù)據(jù)庫連接報 java.sql.SQLException: Unknown database。使用 mysql -u root -p mydb.sql 導(dǎo)入前如果 mydb.sql 里沒有 CREATE DATABASE或者當(dāng)前用戶沒權(quán)限建庫表就落在系統(tǒng)庫里甚至沒落進去但 JDBC URL 里卻寫了 xianfeng_db結(jié)果就是連接報 Unknown database。解決先手動執(zhí)行 CREATE DATABASE xianfeng_db DEFAULT CHARSET utf8mb4再進入該庫導(dǎo)入腳本或者直接在 mysql 命令行用 source 方式導(dǎo)入 mydb.sql。5.2 代碼運行與數(shù)據(jù)層面的常見踩坑點現(xiàn)象一前端中文亂碼界面顯示“錕斤拷”。原因有三個位置必須同時設(shè)置 UTF-8JSP 頁面頂部的 pageEncoding、servlet 里 request.setCharacterEncoding(utf-8)、jdbc 連接串里的 characterEncodingutf8。少一個就亂這是老項目的連環(huán)坑。解決三個位置全部補齊product 添加頁面和注冊頁面的表單都過一遍。現(xiàn)象二圖片上傳后 標簽顯示裂圖刷新后圖片丟失。原因開發(fā)環(huán)境用 getRealPath 把圖片寫到 tomcat 部署目錄下的 upload 里但 IDEA 重啟或 clean 后整個部署目錄被重建上傳的文件隨之被刪除。這是 tomcat explode 部署的經(jīng)典副作用。解決把上傳路徑改成工程外目錄比如 D:/upload然后通過映射或單獨 servlet 提供圖片訪問。如果只是跑課程設(shè)計接受重啟丟圖片的現(xiàn)實也行但心里要清楚這個邊界。現(xiàn)象三過濾器把登錄頁的 css/js 也攔截了頁面雖然能打開但是光禿禿的沒有樣式。原因WebFilter(/*) 匹配所有請求css 和 js 也在攔截范圍內(nèi)session 為空時全部重定向到 index.jsp最終瀏覽器加載靜態(tài)資源時循環(huán)重定向。我在 4.3 節(jié)里已經(jīng)寫了放行邏輯的完整代碼把路徑判斷提前到 session 判斷之前問題就解決了?,F(xiàn)象四分頁點下一頁時說 pageIndex 為 null 的 NumberFormatException或者總頁數(shù)算出來是 1 但數(shù)據(jù)有 50 條。原因servlet 里直接 Integer.parseInt(request.getParameter(pageIndex))第一頁時鏈接沒帶參數(shù)就報空指針總頁數(shù)用除法直接取整10 條數(shù)據(jù) pageSize10 時總頁數(shù)算成 1恰好第 11 條就消失了。解決parseInt 前先判空并給默認值 1總頁數(shù)用 (total pageSize - 1) / pageSize 計算。6. 進階改造把 jdbc 直連換成 Druid 連接池順便解決連接資源釋放問題如果你準備把這份源碼改造成能拿去實習(xí)面試里講的工程第一個要動刀的地方絕對不是界面美化而是數(shù)據(jù)源。原生 jdbc 最尷尬的場景就是每次請求都 DriverManager.getConnection用 complete 關(guān) Connection、Statement、ResultSet代碼丑且容易漏關(guān)。我的習(xí)慣是引入 Druid把數(shù)據(jù)源初始化擠到 static 塊里讓整個 webapp 只維護一個連接池。先在 lib 目錄放 druid-1.1.24.jar然后在 src 下加一個 DBUtil 改造版import com.alibaba.druid.pool.DruidDataSource; public class DBUtil { private static DruidDataSource dataSource; static { dataSource new DruidDataSource(); dataSource.setDriverClassName(com.mysql.jdbc.Driver); dataSource.setUrl(jdbc:mysql://localhost:3306/xianfeng_db?useUnicodetruecharacterEncodingutf8); dataSource.setUsername(root); dataSource.setPassword(123456); dataSource.setInitialSize(3); dataSource.setMaxActive(10); dataSource.setMaxWait(3000); } public static Connection getConnection() throws SQLException { return dataSource.getConnection(); } }邏輯說明Druid 的 setInitialSize 是啟動時預(yù)建的連接數(shù)setMaxActive 是池中最多同時活躍的連接數(shù)setMaxWait 是拿不到連接時最長等待毫秒數(shù)。這樣就省去了每個 dao 方法里 DriverManager.getConnection 的重復(fù)代碼連接獲取速度也快了一個數(shù)量級。原有 dao 層的 try-with-resources 不用動因為 Druid 的 getConnection 返回的是代理連接調(diào)用 close 只是歸還池子不會真的斷開。dao 層代碼里所有 Class.forName 加載驅(qū)動的語句都可以刪掉了數(shù)據(jù)源初始化時已經(jīng)完成。接下來把這段改動跑一遍回歸登錄、添加產(chǎn)品、上傳圖片、分頁瀏覽、退出五個鏈路全部過一遍重點觀察 Tomcat 控制臺有沒有連接泄漏提示。改完你會發(fā)現(xiàn)兩件事第一頁面響應(yīng)速度在連續(xù)點擊分頁時明顯變快因為連接復(fù)用而不是每次新建第二Tomcat 重啟時再也不會因為連接未釋放報警。從那以后我每次拿到這種課程設(shè)計源碼都會強制先做一遍數(shù)據(jù)源改造再談功能順手也會把過濾器放行路徑完整走查一遍。這套方法希望你也能用上幫你少走彎路。本文還有配套的精品資源點擊獲取