:Servlet+JSP全棧開(kāi)發(fā)實(shí)戰(zhàn)解析)
簡(jiǎn)介在Java Web開(kāi)發(fā)中從零搭建一個(gè)可運(yùn)行的管理系統(tǒng)往往涉及前端交互、后端服務(wù)與數(shù)據(jù)庫(kù)設(shè)計(jì)的完整鏈路。從用戶(hù)注冊(cè)登錄、商品瀏覽、購(gòu)物車(chē)結(jié)算到訂單生成與庫(kù)存扣減每一環(huán)節(jié)都考驗(yàn)開(kāi)發(fā)者對(duì)業(yè)務(wù)邏輯和技術(shù)棧的掌握。為了理解企業(yè)級(jí)項(xiàng)目中的工程實(shí)踐開(kāi)發(fā)者常借助經(jīng)典技術(shù)組合如Servlet與JSP結(jié)合JDBC實(shí)現(xiàn)數(shù)據(jù)庫(kù)操作并通過(guò)JavaScript與CSS優(yōu)化交互和布局。本文以零食商店管理系統(tǒng)為例詳細(xì)拆解數(shù)據(jù)表設(shè)計(jì)、登錄鑒權(quán)、購(gòu)物車(chē)與下單事務(wù)、高并發(fā)下庫(kù)存扣減、前端異步交互等關(guān)鍵實(shí)現(xiàn)并分享中文亂碼、會(huì)話(huà)失效等聯(lián)調(diào)排錯(cuò)經(jīng)驗(yàn)幫助讀者掌握J(rèn)ava全棧開(kāi)發(fā)的實(shí)用技巧最終快速跑通并擴(kuò)展系統(tǒng)。1. 第一步拆解零食商店管理系統(tǒng)的功能需求1.1 這套系統(tǒng)到底要解決什么問(wèn)題我接觸過(guò)不少初學(xué)Java的人凡是拿“商城”或“管理系統(tǒng)”練手的第一版都容易做成一個(gè)只有增刪改查的玩具。真正落到零食商店這個(gè)場(chǎng)景時(shí)事情會(huì)變得具體得多零食品類(lèi)多、價(jià)格變動(dòng)頻繁、促銷(xiāo)活動(dòng)多、用戶(hù)對(duì)購(gòu)物體驗(yàn)很敏感這些都會(huì)逼著你去思考業(yè)務(wù)細(xì)節(jié)。零食商店管理系統(tǒng)簡(jiǎn)單說(shuō)就是把線(xiàn)下小賣(mài)部的業(yè)務(wù)搬到網(wǎng)頁(yè)上用戶(hù)能注冊(cè)登錄、瀏覽商品、把零食加入購(gòu)物車(chē)、下單買(mǎi)走管理員能維護(hù)商品信息、分類(lèi)、庫(kù)存、訂單狀態(tài)。它最大的價(jià)值在于覆蓋了一條非常完整的業(yè)務(wù)鏈路——從“用戶(hù)看到商品”到“商品扣庫(kù)存發(fā)貨”中間涉及數(shù)據(jù)表設(shè)計(jì)、會(huì)話(huà)管理、前端交互、訂單事務(wù)而這些正是企業(yè)級(jí)項(xiàng)目里最常碰到的場(chǎng)景。我給這套系統(tǒng)定的功能邊界是這樣的前臺(tái)限普通用戶(hù)使用包含注冊(cè)、登錄、首頁(yè)推薦位、商品分類(lèi)瀏覽、商品搜索、商品詳情、購(gòu)物車(chē)、訂單結(jié)算、個(gè)人訂單列表后臺(tái)限管理員使用包含商品管理、分類(lèi)管理、庫(kù)存調(diào)整、訂單處理。所有頁(yè)面都基于Java后端渲染或提供數(shù)據(jù)接口前端用JavaScript處理交互CSS負(fù)責(zé)排版和視覺(jué)反饋三者各司其職。1.2 技術(shù)棧選型Java、JavaScript和CSS各管哪一塊技術(shù)選型上我用的是Servlet JSP JDBC這套經(jīng)典組合。為什么不用Spring Boot因?yàn)楹芏鄬W(xué)校課程設(shè)計(jì)和期末項(xiàng)目考察的重點(diǎn)是你能不能把一條請(qǐng)求從前端送到后端、再回到頁(yè)面如果把Spring Boot的自動(dòng)配置和注解一蓋鏈路就變黑了。用Servlet JSP能看到 request → Servlet → Service → DAO → 數(shù)據(jù)庫(kù) → JSP → 瀏覽器 的全過(guò)程這是“設(shè)計(jì)源碼”四個(gè)字最該體現(xiàn)的內(nèi)容。Java負(fù)責(zé)服務(wù)端所有業(yè)務(wù)規(guī)則。用戶(hù)提交登錄表單Java去數(shù)據(jù)庫(kù)校驗(yàn)用戶(hù)名密碼管理員改庫(kù)存Java負(fù)責(zé)并發(fā)安全和事務(wù)控制。JavaScript負(fù)責(zé)瀏覽器端的實(shí)時(shí)反饋購(gòu)物車(chē)數(shù)量加減不刷新頁(yè)面、下單前校驗(yàn)手機(jī)號(hào)格式、AJAX把異步請(qǐng)求發(fā)給后端。CSS負(fù)責(zé)視覺(jué)呈現(xiàn)導(dǎo)航欄、商品卡片、按鈕、定價(jià)標(biāo)簽、彈窗和適配不同尺寸屏幕的響應(yīng)式布局。這套系統(tǒng)最終呈現(xiàn)出來(lái)的效果是Java代碼里不混雜JavaScript邏輯JSP頁(yè)面里不堆砌大段CSS。JS文件用static/js/common.js統(tǒng)一定義公共函數(shù)CSS文件按模塊拆成global.css、layout.css、product.css、admin.css頁(yè)面里只引入需要的文件。這樣項(xiàng)目目錄干凈后期找問(wèn)題也快。2. 數(shù)據(jù)庫(kù)表設(shè)計(jì)把地基打牢的6張核心表2.1 用戶(hù)、商品、分類(lèi)三張基礎(chǔ)表怎么建表結(jié)構(gòu)是整個(gè)系統(tǒng)里最值得反復(fù)琢磨的部分后端的代碼邏輯幾乎都是圍繞表展開(kāi)的。我設(shè)計(jì)的6張核心表分別是user用戶(hù)表、category分類(lèi)表、product商品表、cart購(gòu)物車(chē)表、orders訂單表、order_item訂單明細(xì)表。前3張是基礎(chǔ)表后3張是業(yè)務(wù)表。用戶(hù)表user字段如下id用INT自增主鍵username用VARCHAR(50)且加唯一索引password用VARCHAR(64)存的是加密后的哈希值role用TINYINT0代表普通用戶(hù)1代表管理員phone用VARCHAR(20)address用VARCHAR(255)create_time用DATETIME。密碼字段是我特意強(qiáng)調(diào)的點(diǎn)明文密碼一旦數(shù)據(jù)庫(kù)泄露就是安全事故系統(tǒng)里統(tǒng)一用MD5加鹽后再入庫(kù)鹽值可以用用戶(hù)名拼接固定字符串比如md5(username salt2024 password)。商品表product是信息量最大的一張表id、category_id、name、price、stock、image、description、status、sales、create_time。price用DECIMAL(10,2)而不用FLOAT或DOUBLE原因很簡(jiǎn)單浮點(diǎn)數(shù)在計(jì)算機(jī)里是近似表示1.1 2.2可能等于3.3000000000000003購(gòu)物車(chē)結(jié)算時(shí)總價(jià)會(huì)顯示出一長(zhǎng)串小數(shù)。DECIMAL是以字符串形式存儲(chǔ)的精確數(shù)字類(lèi)型配合Java里BigDecimal使用金額計(jì)算不會(huì)出問(wèn)題。stock用INT每次扣庫(kù)存都必須做條件更新防止并發(fā)下超賣(mài)。2.2 購(gòu)物車(chē)、訂單、訂單明細(xì)三張業(yè)務(wù)表怎么建購(gòu)物車(chē)表cart結(jié)構(gòu)不算復(fù)雜id、user_id、product_id、quantity、create_time。但這里有一個(gè)很容易被忽略的設(shè)計(jì)點(diǎn)同一個(gè)用戶(hù)把同一個(gè)商品加入購(gòu)物車(chē)兩次不應(yīng)該產(chǎn)生兩條記錄而應(yīng)該把quantity累加。我在user_id和product_id上建了聯(lián)合唯一索引數(shù)據(jù)庫(kù)層面保證不重復(fù)代碼里先查詢(xún)?cè)俑码p保險(xiǎn)。訂單表orders的名字是避坑點(diǎn)order是SQL關(guān)鍵字直接用可能會(huì)在某些數(shù)據(jù)庫(kù)版本上報(bào)語(yǔ)法錯(cuò)誤。字段包括id、order_no、user_id、total_amount、status、pay_type、receiver_name、receiver_phone、receiver_address、remark、create_time。order_no是用戶(hù)可見(jiàn)的訂單號(hào)我習(xí)慣用時(shí)間加隨機(jī)數(shù)拼比如yyyyMMddHHmmss加4位隨機(jī)數(shù)雖然理論上有重復(fù)概率但配合數(shù)據(jù)庫(kù)唯一索引一旦插入沖突就重新生成。訂單明細(xì)表order_item尤其要講清楚id、order_id、product_id、product_name、price、quantity、subtotal。為什么重復(fù)存product_name和price這是“快照”思想。用戶(hù)下單后商品可能改價(jià)、改名、甚至下架歷史訂單里的商品信息必須保持下單那一刻的樣子。如果只存product_id兩個(gè)月后訂單詳情頁(yè)顯示的價(jià)格和用戶(hù)當(dāng)初買(mǎi)的價(jià)格對(duì)不上售后問(wèn)題就會(huì)爆炸。這個(gè)細(xì)節(jié)在答辯時(shí)經(jīng)常被老師追問(wèn)答上來(lái)非常加分。3. Java后端核心邏輯實(shí)現(xiàn)從登錄到下單3.1 登錄鑒權(quán)與驗(yàn)證碼校驗(yàn)細(xì)節(jié)登錄接口是系統(tǒng)的入口也是安全防護(hù)的第一道關(guān)卡。我的處理順序是先校驗(yàn)驗(yàn)證碼再校驗(yàn)用戶(hù)名密碼。驗(yàn)證碼的用途是擋自動(dòng)化腳本不是為了增加用戶(hù)體驗(yàn)負(fù)擔(dān)所以校驗(yàn)完必須立刻從Session中移除驗(yàn)證碼防止同一個(gè)驗(yàn)證碼被重復(fù)使用。用戶(hù)不存在和密碼錯(cuò)誤我統(tǒng)一提示“用戶(hù)名或密碼錯(cuò)誤”不區(qū)分具體原因這是防止賬號(hào)枚舉的常用做法。密碼加密我用的方案是MD5加鹽。注意MD5本身并不安全容易被彩虹表破解但加鹽后能大幅提高破解成本。更嚴(yán)謹(jǐn)?shù)淖龇ㄊ怯肂Crypt但純Servlet項(xiàng)目里引入BCrypt依賴(lài)稍微增加復(fù)雜度而且課程設(shè)計(jì)語(yǔ)境下MD5加鹽已經(jīng)足夠體現(xiàn)安全意識(shí)。如果你用Spring Boot強(qiáng)烈建議直接用BCryptPasswordEncoder安全性高一個(gè)檔次。登錄成功后要把user對(duì)象放進(jìn)Session后面每個(gè)頁(yè)面通過(guò)Session判斷是否登錄。角色控制用一個(gè)簡(jiǎn)單的Filter攔截后臺(tái)路徑比如請(qǐng)求/admin/*時(shí)檢查Session里的role是否為1不是就重定向到登錄頁(yè)。Session超時(shí)時(shí)間在web.xml里配置我設(shè)置30分鐘太短會(huì)被用戶(hù)罵太長(zhǎng)有安全隱患。前端AJAX請(qǐng)求還要約定一個(gè)統(tǒng)一狀態(tài)碼比如Session失效時(shí)后端返回code401前端在complete回調(diào)里判斷彈“登錄已過(guò)期”并跳轉(zhuǎn)登錄頁(yè)。3.2 商品列表、購(gòu)物車(chē)和下單的事務(wù)要點(diǎn)商品列表頁(yè)要處理分頁(yè)和篩選分頁(yè)不能把所有數(shù)據(jù)查出來(lái)再在Java里截取數(shù)據(jù)量一大就會(huì)內(nèi)存溢出。SQL層用LIMIT offset, pageSize篩選條件用動(dòng)態(tài)SQL拼接。在Servlet JDBC時(shí)代我用StringBuilder拼接條件但值必須用PreparedStatement的占位符絕對(duì)不能直接拼字符串否則等于把SQL注入漏洞擺在頁(yè)面上。如果項(xiàng)目升級(jí)到MyBatis用 標(biāo)簽和 標(biāo)簽更優(yōu)雅。購(gòu)物車(chē)模塊我采用的是落庫(kù)方案而不是Cookie方案因?yàn)镃ookie長(zhǎng)度有限、容易被篡改、Cookie關(guān)閉就丟。加入購(gòu)物車(chē)接口要做三件事查商品是否存在并且上架、判斷庫(kù)存是否足夠、插入或更新cart表。數(shù)量加減時(shí)前端通過(guò)AJAX提交新數(shù)量后端接收后必須重新校驗(yàn)quantity的合法性不能信任前端傳過(guò)來(lái)的任何值。下單是整個(gè)系統(tǒng)最核心的代碼。我把它嚴(yán)格切成四步第一步校驗(yàn)參數(shù)包括商品是否存在、庫(kù)存夠不夠、收貨人信息是否完整第二步插入訂單主表生成order_no和total_amount第三步遍歷購(gòu)物車(chē)明細(xì)逐條插入訂單明細(xì)表并扣減庫(kù)存第四步清空購(gòu)物車(chē)。任何一步失敗都要整體回滾否則會(huì)出現(xiàn)訂單生成了但庫(kù)存沒(méi)扣、購(gòu)物車(chē)沒(méi)清空的臟數(shù)據(jù)??蹘?kù)存這一步有一個(gè)高并發(fā)下非常關(guān)鍵的小技巧。不能先SELECT stock再UPDATE stock stock - ?,因?yàn)閮蓷l并發(fā)請(qǐng)求可能同時(shí)讀到舊的庫(kù)存值導(dǎo)致超賣(mài)。正確寫(xiě)法是UPDATE product SET stock stock - ? WHERE id ? AND stock ?數(shù)據(jù)庫(kù)層面的條件更新會(huì)在行鎖下串行執(zhí)行只有庫(kù)存足夠時(shí)才更新成功受影響行數(shù)為0就說(shuō)明庫(kù)存不足。這套寫(xiě)法在實(shí)際電商項(xiàng)目里也很常見(jiàn)。4. JavaScript與CSS前端實(shí)現(xiàn)交互和布局的關(guān)鍵細(xì)節(jié)4.1 JavaScript購(gòu)物車(chē)交互數(shù)量加減、總價(jià)計(jì)算、表單校驗(yàn)Java后端寫(xiě)得再漂亮用戶(hù)感知最深的還是頁(yè)面交互。購(gòu)物車(chē)頁(yè)面的數(shù)量加減是JavaScript最典型的使用場(chǎng)景。用戶(hù)點(diǎn)擊加號(hào)按鈕數(shù)量加1小計(jì)和總價(jià)同步更新點(diǎn)擊減號(hào)時(shí)數(shù)量不能小于1到了庫(kù)存上限時(shí)加號(hào)按鈕要禁用。這些交互必須用異步請(qǐng)求不能每次點(diǎn)擊都刷新整個(gè)頁(yè)面。AJAX請(qǐng)求返回的數(shù)據(jù)格式我統(tǒng)一用一個(gè)Result對(duì)象封裝code、message、data。前端用jQuery的$.ajax設(shè)置dataType: json,成功回調(diào)里先判斷codecode為200再更新DOM。這里有個(gè)非常常見(jiàn)的坑后端返回的JSON里數(shù)字字段如果帶了引號(hào)前端拿到的是字符串做加法時(shí)直接變成拼接比如1 1結(jié)果是11。我寫(xiě)了個(gè)toMoney函數(shù)先轉(zhuǎn)成Number再乘以100取整計(jì)算最后除以100并用toFixed(2)格式化從根源避免浮點(diǎn)數(shù)精度問(wèn)題。JavaScript里還有兩個(gè)面試高頻問(wèn)題在項(xiàng)目里會(huì)真實(shí)遇到。第一個(gè)是for循環(huán)里綁定點(diǎn)擊事件的閉包問(wèn)題用var聲明循環(huán)變量時(shí)循環(huán)結(jié)束后i永遠(yuǎn)是最后一個(gè)值點(diǎn)擊任何按鈕都執(zhí)行最后一次邏輯解決辦法是用let聲明或者用立即執(zhí)行函數(shù)包一層。第二個(gè)是隱式轉(zhuǎn)換[] ![]這個(gè)經(jīng)典題目網(wǎng)上的分析很多但我的原則更簡(jiǎn)單項(xiàng)目里一律使用除非有極其明確的理由比如判斷null和undefined可以簡(jiǎn)寫(xiě)成 null。規(guī)范寫(xiě)代碼比背這些結(jié)論重要得多。表單校驗(yàn)這塊注冊(cè)頁(yè)和結(jié)算頁(yè)都需要。用戶(hù)名長(zhǎng)度、密碼強(qiáng)度、手機(jī)號(hào)格式、收貨地址是否為空這些都在JavaScript里做第一層校驗(yàn)。但后端一定要再做一次校驗(yàn)因?yàn)榍岸诵r?yàn)只是用戶(hù)體驗(yàn)后端校驗(yàn)才是安全邊界。我見(jiàn)過(guò)有人只在前端限制必填結(jié)果用Postman直接提交空數(shù)據(jù)訂單照樣生成數(shù)據(jù)庫(kù)里一堆臟數(shù)據(jù)。4.2 CSS布局flex、grid、居中與視覺(jué)反饋CSS部分最讓我省心的是flex布局。以前寫(xiě)商品列表用float每排幾個(gè)要精確計(jì)算寬度還有父元素高度塌陷問(wèn)題必須寫(xiě)clearfix?,F(xiàn)在直接用display: flex和flex-wrap: wrap商品卡片自動(dòng)換行排列。導(dǎo)航欄和底部版權(quán)條用justify-content: space-between配合align-items: center左右分布和垂直居中一次搞定?!霸趺凑{(diào)整CSS容器里的文本位置”是搜索熱度很高的問(wèn)題其實(shí)就那幾板斧。水平居中文本用text-align: center塊級(jí)元素用margin: 0 autoflex容器里用justify-content: center。垂直居中單行文本用line-height等于容器高度任意元素在flex容器里用align-items: center。商品卡片的標(biāo)題如果超過(guò)兩行要顯示省略號(hào)我用display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden這套屬性在主流瀏覽器上都穩(wěn)定。后臺(tái)管理頁(yè)面我用的display: grid定義數(shù)據(jù)表格和表單布局grid比f(wàn)lex更適合二維布局比如分類(lèi)管理的左側(cè)分類(lèi)列表、右側(cè)商品表格。列寬用grid-template-columns: 200px 1fr實(shí)現(xiàn)左邊固定200px右邊自適應(yīng)。視覺(jué)反饋上按鈕hover時(shí)輕微上浮用transform: translateY(-2px)加transition: all 0.3s加入購(gòu)物車(chē)后彈一個(gè)小提示用CSS動(dòng)畫(huà)做漣漪效果原理是keyframes里定義transform: scale和opacity變化不需要引入額外組件庫(kù)。促銷(xiāo)標(biāo)題的字體漸變用background: linear-gradient搭配background-clip: text和color: transparent幾行代碼就能讓頁(yè)面看起來(lái)高級(jí)不少。這些細(xì)節(jié)不會(huì)增加后端壓力但會(huì)讓整個(gè)系統(tǒng)的完成度提升一個(gè)檔次。5. 聯(lián)調(diào)實(shí)戰(zhàn)高頻報(bào)錯(cuò)的排查記錄與解決思路5.1 中文亂碼為什么改了還是亂中文亂碼在JSP Servlet項(xiàng)目里幾乎是“必經(jīng)之路”而且更頭疼的是亂碼往往不是單點(diǎn)問(wèn)題。我遇到過(guò)頁(yè)面顯示亂碼、數(shù)據(jù)庫(kù)存出的中文變成問(wèn)號(hào)、AJAX返回的JSON中文亂碼三種場(chǎng)景原因各不相同。頁(yè)面顯示亂碼先看JSP頭部是否同時(shí)設(shè)置了pageEncodingUTF-8和contentTypetext/html;charsetUTF-8少一個(gè)都可能在中文環(huán)境下出問(wèn)題。POST請(qǐng)求的中文亂碼要在Servlet的doPost開(kāi)頭執(zhí)行request.setCharacterEncoding(UTF-8)注意必須在讀取請(qǐng)求參數(shù)之前調(diào)用否則已經(jīng)按默認(rèn)編碼解析完了再設(shè)置也白搭。數(shù)據(jù)庫(kù)中文變問(wèn)號(hào)檢查連接URL有沒(méi)有useUnicodetruecharacterEncodingutf8以及數(shù)據(jù)庫(kù)本身字符集是不是utf8mb4。有一次排查亂碼了很久最后發(fā)現(xiàn)是MySQL配置文件my.cnf里的character-set-server還是latin1。先執(zhí)行show variables like %char%;看到一堆latin1就說(shuō)明server層沒(méi)改對(duì)改完my.cnf后必須重啟MySQL服務(wù)。我當(dāng)時(shí)的排查順序是瀏覽器按F12看響應(yīng)頭字符集 → 后端設(shè)置斷點(diǎn)看參數(shù)值 → 直接命令行查詢(xún)數(shù)據(jù)庫(kù)里的值一步定位到server字符集。以后遇到亂碼建議也按這個(gè)順序排。5.2 404、會(huì)話(huà)失效與JVM內(nèi)存不足的排查404問(wèn)題分兩類(lèi)。一類(lèi)是Servlet路徑寫(xiě)錯(cuò)比如映射寫(xiě)的是/product/list頁(yè)面卻請(qǐng)求/product/list/,多一個(gè)斜杠或者少一層路徑都會(huì)404。更隱蔽的是JSP頁(yè)面在WEB-INF目錄下瀏覽器直接訪(fǎng)問(wèn)不到URL必須通過(guò)Servlet轉(zhuǎn)發(fā)。另一類(lèi)是Spring Boot項(xiàng)目的靜態(tài)資源404CSS和JS文件請(qǐng)求不到我在實(shí)戰(zhàn)中排查定位到兩個(gè)原因文件沒(méi)放在src/main/resources/static目錄下或者路徑以/static開(kāi)頭重復(fù)寫(xiě)了一遍。會(huì)話(huà)失效問(wèn)題是聯(lián)調(diào)時(shí)的高頻Bug。用戶(hù)在一個(gè)頁(yè)面停留很久Session過(guò)期后再點(diǎn)任何按鈕AJAX請(qǐng)求返回的不是JSON而是登錄頁(yè)面HTML前端JSON.parse直接報(bào)錯(cuò)。我的統(tǒng)一處理方案是后端攔截器中判斷Session為空時(shí)返回code401,前端在$.ajax的complete回調(diào)里檢查這個(gè)code統(tǒng)一跳轉(zhuǎn)登錄頁(yè)。這樣用戶(hù)不會(huì)看到白屏或控制臺(tái)報(bào)錯(cuò)體驗(yàn)會(huì)好很多。還有一個(gè)容易被忽略的運(yùn)行錯(cuò)誤java.lang.OutOfMemoryError: insufficient memory。開(kāi)發(fā)工具默認(rèn)的JVM堆內(nèi)存一般不大如果項(xiàng)目里把大量商品數(shù)據(jù)塞進(jìn)Session做緩存或者循環(huán)里創(chuàng)建大對(duì)象沒(méi)釋放內(nèi)存很快就爆。調(diào)大VM options可以緩解比如IDEA里配置-Xms256m -Xmx1024m -Dfile.encodingUTF-8但根本還是要審視代碼里有沒(méi)有不該長(zhǎng)期存活的大對(duì)象。我的原則是Session里只放user對(duì)象等必要數(shù)據(jù)商品列表和搜索推薦數(shù)據(jù)實(shí)時(shí)查數(shù)據(jù)庫(kù)加一層短時(shí)效緩存都比放在Session里靠譜。6. 部署與二次開(kāi)發(fā)拿到源碼后怎么快速跑通6.1 本地環(huán)境搭建與打包部署流程拿到這套源碼先不要急著改代碼第一步是讓項(xiàng)目在本地完整跑起來(lái)。環(huán)境要求JDK 8以上、Tomcat 8.5或9、MySQL 5.7以上、Maven 3.6以上。導(dǎo)入IDEA后如果項(xiàng)目是老式的Servlet JSP結(jié)構(gòu)要把項(xiàng)目配置成War包部署到Tomcat運(yùn)行如果是Spring Boot版本直接運(yùn)行主類(lèi)即可。數(shù)據(jù)庫(kù)初始化這一步經(jīng)常有人卡住。項(xiàng)目里我配了一個(gè)init.sql腳本包含建庫(kù)、建表、插入測(cè)試數(shù)據(jù)。在命令行執(zhí)行mysql -u root -p init.sql或者用Navicat導(dǎo)入。供應(yīng)商給的測(cè)試數(shù)據(jù)不能刪尤其管理員賬號(hào)、幾十條零食商品數(shù)據(jù)刪了以后你就沒(méi)有數(shù)據(jù)可以看出頁(yè)面效果。賬號(hào)密碼加密邏輯要特別注意init.sql里插入的密碼哈希值和項(xiàng)目里的加密工具類(lèi)是否一致不一致會(huì)導(dǎo)致你永遠(yuǎn)無(wú)法用初始賬號(hào)登錄。部署到服務(wù)器時(shí)Spring Boot項(xiàng)目用mvn clean package打成jar包java -jar app.jar啟動(dòng)端口通過(guò)--server.port8081參數(shù)修改。傳統(tǒng)War包則要放進(jìn)Tomcat的webapps目錄啟動(dòng)后自動(dòng)解壓部署。部署后驗(yàn)證三件事數(shù)據(jù)庫(kù)連接池配置是否指向了服務(wù)器的MySQL、服務(wù)器防火墻是否放行了對(duì)應(yīng)端口、瀏覽器能訪(fǎng)問(wèn)首頁(yè)并完成一個(gè)完整的登錄和下單流程。6.2 源碼閱讀順序與擴(kuò)展方向建議源碼不是小說(shuō)不建議從頭到尾逐行讀。我推薦的閱讀順序是先看數(shù)據(jù)庫(kù)結(jié)構(gòu)再讀實(shí)體類(lèi)和工具類(lèi)接著走一遍Service層最后看Servlet/Controller和JSP頁(yè)面。這個(gè)順序能幫你先建立“數(shù)據(jù)長(zhǎng)什么樣”的認(rèn)知再理解“代碼如何操作這些數(shù)據(jù)”。重點(diǎn)跟蹤三條鏈路登錄鏈路、商品查詢(xún)鏈路、下單鏈路。三條鏈路走通整個(gè)系統(tǒng)的架構(gòu)就清晰了。這套系統(tǒng)的擴(kuò)展空間其實(shí)很大。給product表加一個(gè)is_recommend字段首頁(yè)就能做推薦位把訂單狀態(tài)從“模擬支付”改成調(diào)用支付接口就更接近商業(yè)項(xiàng)目。我自己在原來(lái)的版本上擴(kuò)展過(guò)一個(gè)積分模塊用戶(hù)下單后獲得積分積分可以在結(jié)算時(shí)抵扣現(xiàn)金。這個(gè)需求看起來(lái)簡(jiǎn)單實(shí)際要改訂單表、積分明細(xì)表、結(jié)算頁(yè)、訂單詳情頁(yè)做完后對(duì)事務(wù)一致性和模塊解耦的理解會(huì)深很多。我在實(shí)際帶人看這套源碼時(shí)發(fā)現(xiàn)最快的上手方式不是看文檔而是改一個(gè)小功能。比如把商品列表從“按創(chuàng)建時(shí)間排序”改成“按銷(xiāo)量排序”哪怕只改一行SQL也會(huì)倒逼你去找到DAO層、Service層、Controller層之間的調(diào)用關(guān)系。改完第一個(gè)功能后面就順了。最后分享一點(diǎn)個(gè)人體會(huì)這套零食商店管理系統(tǒng)從頭跑完我最大的感觸是代碼本身不復(fù)雜復(fù)雜的是把前端交互、后端邏輯、數(shù)據(jù)庫(kù)狀態(tài)串成一條不掉的線(xiàn)。購(gòu)物車(chē)加一減一表面是JavaScript改數(shù)字背后是AJAX請(qǐng)求、接口校驗(yàn)、數(shù)據(jù)庫(kù)更新和前端回調(diào)的完整閉環(huán)下單按鈕點(diǎn)一次背后是訂單生成、明細(xì)快照、庫(kù)存扣減、購(gòu)物車(chē)清空的事務(wù)鏈條。把這些鏈路搞清楚比單純背概念有用得多。如果你現(xiàn)在剛拿到這套源碼我建議你先跑通再跟斷點(diǎn)最后動(dòng)手改一個(gè)需求。遇到問(wèn)題別急著查解決方案先看一眼后端日志和控制臺(tái)報(bào)錯(cuò)很多答案已經(jīng)在里面了。本文還有配套的精品資源點(diǎn)擊獲取