人博客系統(tǒng)畢設(shè):Spring Boot+MyBatis架構(gòu)與部署答辯全攻略)
簡(jiǎn)介一份基于Java的個(gè)人博客系統(tǒng)畢業(yè)設(shè)計(jì)全套資料包面向計(jì)算機(jī)相關(guān)專業(yè)畢業(yè)生與Java Web初學(xué)者可覆蓋選題、開發(fā)、文檔整理與答辯展示的完整需求尤其適合需要從零搭建完整項(xiàng)目的學(xué)習(xí)者。壓縮包大小178.52MB內(nèi)含項(xiàng)目報(bào)告、答辯PPT、源代碼、數(shù)據(jù)庫(kù)文件與部署教程視頻對(duì)應(yīng)需求分析、系統(tǒng)設(shè)計(jì)、模塊劃分、接口定義、數(shù)據(jù)表結(jié)構(gòu)及核心編碼實(shí)現(xiàn)等核心資料。技術(shù)棧涉及Servlet、JSP、JavaBean、MySQL等后端與數(shù)據(jù)庫(kù)技術(shù)以及HTML、CSS、JavaScript、Bootstrap等前端技術(shù)代碼分層清晰便于理解MVC架構(gòu)下的控制層、業(yè)務(wù)層與持久層。目前已有1347人瀏覽學(xué)習(xí)適合作為畢業(yè)設(shè)計(jì)參考和Java Web實(shí)戰(zhàn)練習(xí)。通過(guò)部署視頻可快速完成環(huán)境配置、數(shù)據(jù)庫(kù)導(dǎo)入與項(xiàng)目啟動(dòng)配合答辯PPT和項(xiàng)目報(bào)告能直觀呈現(xiàn)系統(tǒng)功能與設(shè)計(jì)思路幫助學(xué)習(xí)者少走彎路并提升工程實(shí)踐能力尤其對(duì)系統(tǒng)測(cè)試與部署環(huán)節(jié)可提供直接參照。1. 基于 Java 的個(gè)人博客系統(tǒng)畢業(yè)設(shè)計(jì)這份資源能幫你省掉哪一段路每年畢設(shè)季問得最多的就是「有沒有一個(gè)能直接跑的 Java 項(xiàng)目」這套基于 Java 的個(gè)人博客系統(tǒng)畢業(yè)設(shè)計(jì)資源包——項(xiàng)目報(bào)告 答辯 PPT 源代碼 數(shù)據(jù)庫(kù) 部署視頻——就是沖著這個(gè)需求來(lái)的。它的價(jià)值不是讓你什么都不干而是把搭框架、建庫(kù)、配環(huán)境的幾周時(shí)間壓縮成幾天把精力留給真正要命的事看懂表結(jié)構(gòu)、改出自己講得明白的代碼、練到敢現(xiàn)場(chǎng)演示。適合 Java 方向?qū)W生、想快速交差的初級(jí)開發(fā)者以及用博客練 Spring Boot 的入門選手。下面我按「架構(gòu)立住 → 跑起來(lái) → 改得動(dòng) → 扛住答辯」的順序把這套資源吃透。2. 把博客畢設(shè)拆開看技術(shù)選型、表結(jié)構(gòu)與文檔框架先立住2.1 為什么是 Spring Boot MyBatis選型理由要能扛住答辯提問拿到源碼第一步不是跑而是先搞清楚它為什么長(zhǎng)這樣。常見的 Java 個(gè)人博客模板有兩類老一點(diǎn)的是 SSMSpring Spring MVC MyBatis新一點(diǎn)的是 Spring Boot MyBatis 或 Spring Boot JPA?,F(xiàn)在的主流是 Spring Boot 2.x MyBatis理由很實(shí)際Spring Boot 內(nèi)嵌 Tomcat、自動(dòng)配置數(shù)據(jù)源、打 jar 直接跑省去 SSM 那一大堆 XML 配置MyBatis 的 SQL 是自己寫的映射關(guān)系直觀答辯時(shí)隨便問一句「你的查詢?cè)趺磳憽鼓隳墚?dāng)場(chǎng)說(shuō)出 SQL 而不是解釋一個(gè)黑匣子。為什么不用 JPA畢設(shè)規(guī)模下 JPA 的自動(dòng)建表和懶加載反而是負(fù)擔(dān)尤其是多表關(guān)聯(lián)時(shí) Hibernate 的 N1 查詢問題講不清楚容易翻車。MyBatis 加一個(gè) MyBatis-Plus 的 BaseMapper單表增刪改查幾乎不用寫 SQL代碼量砍一半。模板引擎這邊老項(xiàng)目用 JSP 的不少但現(xiàn)在答辯老師普遍反感 JSP 的部署方式——改一個(gè)頁(yè)面要重新編譯。我一般建議認(rèn)準(zhǔn) Thymeleaf后端渲染、天然支持 Spring Boot、改頁(yè)面只要改模板文件刷新瀏覽器就能看到效果。前后端分離的 Vue REST 方案在工程上是更現(xiàn)代的但對(duì)畢設(shè)來(lái)說(shuō)要額外處理跨域、Token、前端打包演示環(huán)境一多踩坑成本翻倍。2.2 五張核心表文章、分類、標(biāo)簽、評(píng)論、用戶的關(guān)系一次理清數(shù)據(jù)庫(kù)是整個(gè)項(xiàng)目里最值得花時(shí)間搞懂的部分。一個(gè)標(biāo)準(zhǔn)的 Java 個(gè)人博客系統(tǒng)表結(jié)構(gòu)逃不出這五張用戶表、文章表、分類表、標(biāo)簽表、評(píng)論表加上一張文章和標(biāo)簽的多對(duì)多關(guān)聯(lián)表。先建庫(kù)再建表。下面是完整可執(zhí)行的 MySQL 腳本我按通常的模板寫法給你拆開。-- 用戶表 CREATE TABLE t_user ( id INT NOT NULL AUTO_INCREMENT COMMENT 用戶ID, username VARCHAR(50) NOT NULL COMMENT 登錄名, password VARCHAR(100) NOT NULL COMMENT 密碼推薦BCrypt密文, nickname VARCHAR(50) DEFAULT NULL COMMENT 顯示昵稱, role TINYINT NOT NULL DEFAULT 1 COMMENT 權(quán)限1管理員 0普通用戶, create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT 創(chuàng)建時(shí)間, PRIMARY KEY (id), UNIQUE KEY uk_username (username) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT用戶表;-- 文章表 CREATE TABLE t_article ( id INT NOT NULL AUTO_INCREMENT COMMENT 文章ID, title VARCHAR(200) NOT NULL COMMENT 標(biāo)題, summary VARCHAR(500) DEFAULT NULL COMMENT 摘要, content LONGTEXT NOT NULL COMMENT 正文存HTML或Markdown, category_id INT DEFAULT NULL COMMENT 所屬分類ID, user_id INT NOT NULL COMMENT 作者ID, view_count INT NOT NULL DEFAULT 0 COMMENT 瀏覽量, status TINYINT NOT NULL DEFAULT 0 COMMENT 0草稿 1已發(fā)布, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY idx_category (category_id), KEY idx_user (user_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT文章表;-- 分類表 / 標(biāo)簽表 / 評(píng)論表 CREATE TABLE t_category ( id INT NOT NULL AUTO_INCREMENT, name VARCHAR(50) NOT NULL, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_name (name) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT分類表; CREATE TABLE t_tag ( id INT NOT NULL AUTO_INCREMENT, name VARCHAR(50) NOT NULL, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT標(biāo)簽表; CREATE TABLE t_comment ( id INT NOT NULL AUTO_INCREMENT, article_id INT NOT NULL COMMENT 文章ID, user_id INT DEFAULT NULL COMMENT 評(píng)論人ID游客為NULL, content VARCHAR(1000) NOT NULL, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY idx_article (article_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT評(píng)論表;-- 文章-標(biāo)簽關(guān)聯(lián)表 CREATE TABLE t_article_tag ( article_id INT NOT NULL, tag_id INT NOT NULL, PRIMARY KEY (article_id, tag_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT文章標(biāo)簽關(guān)聯(lián)表;幾個(gè)參數(shù)你答辯時(shí)最好能主動(dòng)講出來(lái)。第一engineInnoDB支持事務(wù)和行級(jí)鎖博客這種讀寫混合的小項(xiàng)目足夠第二charsetutf8mb4而不是utf8因?yàn)楸砬榉?hào)也要入庫(kù)utf8 存不下 emoji 會(huì)直接報(bào)錯(cuò)第三create_time用DEFAULT CURRENT_TIMESTAMP插入時(shí)不用手動(dòng)塞時(shí)間第四外鍵我建議不建物理外鍵只建普通索引因?yàn)槲锢硗怄I在刪除分類或用戶時(shí)會(huì)連鎖報(bào)錯(cuò)畢設(shè)階段用邏輯關(guān)聯(lián)在代碼層控制就夠了。第五t_article_tag這種關(guān)聯(lián)表的主鍵用的是聯(lián)合主鍵防止同一篇文章打同一個(gè)標(biāo)簽兩遍。2.3 項(xiàng)目報(bào)告和答辯 PPT三塊內(nèi)容決定你是 60 分還是 90 分源碼再漂亮報(bào)告和 PPT 寫不明白照樣被卡。我的經(jīng)驗(yàn)是無(wú)論模板里原本寫了什么你至少要在這三塊上投入時(shí)間。第一塊是需求分析和用例圖別寫「本系統(tǒng)實(shí)現(xiàn)了用戶管理」這種廢話要寫「管理員可以發(fā)布、編輯、刪除文章游客可以按分類和標(biāo)簽瀏覽文章并發(fā)表評(píng)論」每個(gè)角色配兩三個(gè)典型流程。第二塊是系統(tǒng)設(shè)計(jì)把架構(gòu)圖畫成三層控制層Controller、業(yè)務(wù)層Service、數(shù)據(jù)訪問層Mapper再把 2.2 里的 ER 圖貼上去標(biāo)注主鍵、外鍵和一對(duì)多關(guān)系。第三塊是測(cè)試結(jié)果除了功能測(cè)試用例表強(qiáng)烈建議放兩三個(gè)接口測(cè)試截圖——用 Postman 調(diào) /article/list、/admin/article/save 這些接口把返回 JSON 貼進(jìn) PPT比十頁(yè)功能截圖都有說(shuō)服力。文檔這塊有個(gè)常見翻車點(diǎn)直接從別的畢設(shè)復(fù)制需求分析導(dǎo)致「系統(tǒng)角色」還留著根本沒實(shí)現(xiàn)的后臺(tái)評(píng)論審核功能。答辯老師對(duì)項(xiàng)目里沒有的東西問得最狠。我建議對(duì)著自己的功能清單一項(xiàng)項(xiàng)核報(bào)告里出現(xiàn)的每個(gè)功能代碼里必須能找到對(duì)應(yīng)入口代碼里有的功能報(bào)告里必須有截圖。能做到這個(gè)閉環(huán)答辯基本就穩(wěn)了。3. 從源碼到跑起來(lái)部署視頻沒拍全的關(guān)鍵步驟與參數(shù)3.1 環(huán)境準(zhǔn)備JDK、Maven、MySQL 的版本匹配關(guān)系部署視頻通常會(huì)快進(jìn)這段但環(huán)境不匹配是「跑不起來(lái)」的第一大原因這不是玄學(xué)是版本兼容問題。先把三個(gè)版本對(duì)清楚再動(dòng)手。java -version mvn -version mysql --version三條命令分別查 JDK、Maven 和 MySQL 的版本。常見的 Java 個(gè)人博客模板對(duì)應(yīng)的是 JDK 1.8 Maven 3.6 MySQL 5.7 或 8.0。如果你電腦上裝的是 JDK 17直接跑 Spring Boot 2.x 老模板啟動(dòng)時(shí)大概率報(bào)UnsupportedClassVersionError或者 CGLIB 相關(guān)錯(cuò)誤因?yàn)樾掳?JDK 移除了部分反射權(quán)限。兩個(gè)處理辦法一是裝一個(gè) JDK 1.8 并在 IDEA 的 Project Structure 里切過(guò)去二是把 pom.xml 里的 Spring Boot 版本升到 2.7 以上同時(shí)檢查 MyBatis 和 Thymeleaf 的兼容性。我一般傾向第一種成本最低也最貼近模板原作者的運(yùn)行環(huán)境。MySQL 這塊注意一個(gè)小細(xì)節(jié)5.7 和 8.0 的驅(qū)動(dòng)類名不一樣。5.7 用com.mysql.jdbc.Driver8.0 用com.mysql.cj.jdbc.Driver而且 8.0 的連接 URL 必須帶時(shí)區(qū)參數(shù)不然報(bào)Communications link failure。下面配置章節(jié)我會(huì)把標(biāo)準(zhǔn)寫法給出來(lái)。如果你用 IDEA直接把源碼目錄當(dāng) Maven 項(xiàng)目導(dǎo)入等依賴下載完成src/main/java下的啟動(dòng)類會(huì)有一個(gè)綠色的運(yùn)行按鈕點(diǎn)了就能起——但先別急數(shù)據(jù)庫(kù)還沒導(dǎo)。3.2 導(dǎo)入數(shù)據(jù)庫(kù)先建庫(kù)再導(dǎo)數(shù)據(jù)別跳過(guò) charset資源包里通常會(huì)有一個(gè).sql文件可能是blog.sql或database.sql。導(dǎo)入前先做一件事打開看看文件開頭有沒有CREATE DATABASE語(yǔ)句。有的話直接一條命令全導(dǎo)沒有的話先建庫(kù)再導(dǎo)。mysql -u root -p -e CREATE DATABASE IF NOT EXISTS blog DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_general_ci; mysql -u root -p blog blog.sql第一行創(chuàng)建名為 blog 的數(shù)據(jù)庫(kù)DEFAULT CHARSET 和 COLLATE 必須顯式指定因?yàn)橛行?MySQL 服務(wù)器的默認(rèn)字符集是 latin1表即使寫了 utf8mb4建庫(kù)這一步兜底錯(cuò)了后面查中文會(huì)亂碼。第二行把 SQL 文件導(dǎo)入到 blog 庫(kù)。成功后執(zhí)行mysql -u root -p -e USE blog; SHOW TABLES;能看到 t_user、t_article 這些表就對(duì)了。還有一坑有些模板的 SQL 文件里已經(jīng)寫了USE blog;但你本地庫(kù)名不叫 blog 叫 blog_system導(dǎo)入時(shí)就會(huì)把數(shù)據(jù)寫進(jìn)它指定的庫(kù)你的配置文件卻連著一個(gè)空庫(kù)。遇到這種情況要么建一個(gè)和文件里同名的庫(kù)要么把 SQL 文件里的USE語(yǔ)句注釋掉再導(dǎo)入。導(dǎo)入完了順手查一下 t_user 表有沒有初始賬號(hào)數(shù)據(jù)沒有的話自己插入一條管理員賬號(hào)密碼先用最簡(jiǎn)單的 123456登錄跑通后再改。3.3 application.yml 的五個(gè)必調(diào)參數(shù)改錯(cuò)一個(gè)就起不來(lái)源碼里在src/main/resources下找到application.yml或application.properties。Spring Boot 2.x 模板基本都用 yml 格式層次比 properties 清楚。下面是按標(biāo)準(zhǔn)寫法給出的最小可用配置server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/blog?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: 你自己的密碼 driver-class-name: com.mysql.cj.jdbc.Driver thymeleaf: cache: false mybatis-plus: mapper-locations: classpath:mapper/*.xml configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl五個(gè)重點(diǎn)逐一說(shuō)明。第一server.port改 8080 以外要保證沒被占用。第二URL 里serverTimezoneAsia/Shanghai是 MySQL 8.0 的必填項(xiàng)缺了直接連接失敗。第三characterEncodingutf8配合數(shù)據(jù)庫(kù)的 utf8mb4 保證中文不亂碼URL 里的useUnicodetrue也順手留著。第四spring.thymeleaf.cache: false是開發(fā)期福利改 HTML 模板不重啟就能看到效果如果設(shè)成 true你改了首頁(yè)死活沒變化就是這個(gè)在作怪。第五log-impl配成 StdOutImpl 后每次 SQL 執(zhí)行都會(huì)打印到控制臺(tái)調(diào)試和答辯排錯(cuò)都靠它。密碼字段提醒一句本地開發(fā)可以明文但不要把這個(gè)配置提交到公開的 GitHub 倉(cāng)庫(kù)源代碼管理里把配置文件的敏感信息替換成環(huán)境變量是基本素質(zhì)。3.4 本地啟動(dòng)和打包mvn 兩種方式的區(qū)別環(huán)境配好、庫(kù)導(dǎo)好、配置改好就可以啟動(dòng)了。IDEA 里直接點(diǎn)運(yùn)行最省事但更推薦命令行方式因?yàn)槟阋院笠獙懖渴鹞臋n里面所有命令必須是命令行可執(zhí)行的。mvn spring-boot:run這是開發(fā)模式前臺(tái)運(yùn)行按 CtrlC 結(jié)束日志實(shí)時(shí)滾。適合本地調(diào)代碼。mvn clean package -DskipTests java -jar target/blog-0.0.1-SNAPSHOT.jar這是生產(chǎn)模式先跳過(guò)測(cè)試打包成 jar再java -jar跑起來(lái)。打包完你會(huì)發(fā)現(xiàn) target 目錄下多了個(gè)幾十 MB 的 jar里面帶著內(nèi)嵌 Tomcat扔到任何裝了 JDK 的機(jī)器上都能起這才是部署視頻里常說(shuō)的「可執(zhí)行文件」。啟動(dòng)日志里看到Tomcat started on port(s): 8080就成功了瀏覽器訪問http://localhost:8080如果能打開首頁(yè)整個(gè)鏈路就算跑通。接著做兩件事登錄后臺(tái)然后發(fā)一篇測(cè)試文章確認(rèn)數(shù)據(jù)庫(kù)的增刪改查都是好的。到這一步你已經(jīng)完成了這段畢設(shè)最「虛」但最容易卡住的環(huán)節(jié)。4. 源碼骨架怎么讀、怎么改核心模塊的套路和必調(diào)參數(shù)4.1 登錄與權(quán)限攔截器是最適合畢設(shè)的輕量方案把項(xiàng)目跑起來(lái)后就該打開源碼認(rèn)真讀了。我不建議一開始就從 controller 逐行看而是先順著一個(gè)請(qǐng)求走完你的登錄請(qǐng)求是怎么進(jìn)到后臺(tái)的大多數(shù)個(gè)人博客模板用的是攔截器方案而不是 Spring Security因?yàn)楹笳叩倪^(guò)濾器鏈、UserDetailsService、權(quán)限配置對(duì)畢設(shè)來(lái)說(shuō)太重講不清楚還容易出配置問題。攔截器的實(shí)現(xiàn)就是一個(gè)類加一段注冊(cè)代碼。Component public class LoginInterceptor implements HandlerInterceptor { Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { Object loginUser request.getSession().getAttribute(loginUser); if (loginUser null) { response.sendRedirect(/admin/login); return false; } return true; } }Configuration public class WebConfig implements WebMvcConfigurer { Resource private LoginInterceptor loginInterceptor; Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(loginInterceptor) .addPathPatterns(/admin/**) .excludePathPatterns(/admin/login, /admin/doLogin, /css/**, /js/**, /images/**); } }這段邏輯的關(guān)鍵在excludePathPatterns登錄接口和靜態(tài)資源必須放行否則樣式全丟、登錄接口也進(jìn)不去。真實(shí)模板中這幾個(gè)放行路徑經(jīng)常寫漏寫錯(cuò)表現(xiàn)為「登錄成功了但后臺(tái)頁(yè)面沒有樣式」八成是/css/**這類沒放行或者 JSP 老模板把靜態(tài)資源放在 webapp 下導(dǎo)致路徑對(duì)不上。讀取請(qǐng)求處理流程時(shí)順帶把登錄密碼的校驗(yàn)方式看一眼。好的模板會(huì)存 BCrypt 密文答辯時(shí)可以主動(dòng)提一句「密碼不是明文存儲(chǔ)用的 BCrypt 加鹽哈?!埂H绻愕哪0迨敲魑拇婷艽a我建議改掉在 Service 里用BCryptPasswordEncoder的matches方法做校驗(yàn)改動(dòng)量不大但這是答辯老師最喜歡的細(xì)節(jié)之一。4.2 文章增刪改查MyBatis-Plus 讓代碼少寫一半文章模塊是個(gè)人博客系統(tǒng)的核心你要能講清楚「新增一篇文章從頁(yè)面到數(shù)據(jù)庫(kù)走過(guò)了什么」。常見模板的 Mapper 層寫法是 MyBatis-Plus 風(fēng)格接口長(zhǎng)這樣public interface ArticleMapper extends BaseMapperArticle { }你沒看錯(cuò)空接口就夠了。BaseMapperT內(nèi)置了insert、deleteById、updateById、selectById、selectList等十幾個(gè)通用方法單表 CRUD 零 SQL。真正需要寫條件查詢的地方用的是LambdaQueryWrapper比如后臺(tái)分頁(yè)查文章列表Override public PageArticle pageArticles(int pageNum, int pageSize, String keyword, Long categoryId) { LambdaQueryWrapperArticle wrapper new LambdaQueryWrapper(); wrapper.like(StringUtils.hasText(keyword), Article::getTitle, keyword) .eq(categoryId ! null, Article::getCategoryId, categoryId) .orderByDesc(Article::getCreateTime); return articleMapper.selectPage(new Page(pageNum, pageSize), wrapper); }參數(shù)說(shuō)明like和eq的第一個(gè)參數(shù)是布爾條件為 true 時(shí)該條件才生效這樣搜索框沒填關(guān)鍵詞時(shí)就自動(dòng)跳過(guò)模糊查詢不需要手動(dòng)寫三個(gè) if。Article::getTitle是方法引用MyBatis-Plus 靠它解析成數(shù)據(jù)庫(kù)字段名避免了硬編碼字符串導(dǎo)致改字段名后 SQL 崩掉。selectPage的返回值Page里帶著records當(dāng)前頁(yè)數(shù)據(jù)、total總條數(shù)、current當(dāng)前頁(yè)碼、size每頁(yè)條數(shù)前端遍歷records分頁(yè)組件讀這四個(gè)屬性就夠。答辯時(shí)老師常追問「分頁(yè)怎么實(shí)現(xiàn)的」你要能答出兩層應(yīng)用層用 Page 對(duì)象傳頁(yè)碼和每頁(yè)大小MyBatis-Plus 底層自動(dòng)拼接LIMITMySQL或ROWNUMOracle語(yǔ)句數(shù)據(jù)庫(kù)層返回當(dāng)頁(yè)數(shù)據(jù)而不是全表。如果你的模板不是 MyBatis-Plus 而是純 MyBatis 的 XML 寫法也沒問題去src/main/resources/mapper/下打開 ArticleMapper.xml你會(huì)看到selectPageByCondition這類手寫 SQL那里才是你真正要讀的地方。4.3 前端 Thymeleaf三個(gè)必改頁(yè)面和它們的屬性套路前端模板頁(yè)在src/main/resources/templates/下常見結(jié)構(gòu)是index.html前臺(tái)首頁(yè)、article.html文章詳情、admin/后臺(tái)管理。Thymeleaf 的語(yǔ)法核心就是th:屬性看懂了下面這段首頁(yè)列表其他頁(yè)面都是變體ul li th:eacharticle, stat : ${page.records} a th:href{/article/{id}(id${article.id})} th:text${article.title}文章標(biāo)題/a span th:text${#dates.format(article.createTime, yyyy-MM-dd)}發(fā)布日期/span em th:text共 ${article.viewCount} 次瀏覽瀏覽量/em /li /ulth:each是循環(huán)page.records對(duì)應(yīng) Controller 放進(jìn) Model 的分頁(yè)數(shù)據(jù)stat是循環(huán)計(jì)數(shù)器可以取stat.index拿序號(hào)。th:href的{/article/{id}(id${article.id})}是帶參數(shù)的 URL 寫法比字符串拼接安全自動(dòng)做 URL 編碼。th:text輸出純文本如果文章正文存的是 HTML詳情頁(yè)要用th:utext輸出否則標(biāo)簽會(huì)被轉(zhuǎn)義成文字顯示。這是畢設(shè)里出現(xiàn)頻率最高的 bug正文全部變成了帶p標(biāo)簽的源碼就是因?yàn)橛昧藅h:text。改前端時(shí)兩條血淚經(jīng)驗(yàn)第一頁(yè)面里的靜態(tài)資源路徑一律用th:href{/css/style.css}別在 HTML 里手寫/css/style.css否則項(xiàng)目部署到子路徑時(shí)全部 404第二后臺(tái)表單提交改成 POST 后Controller 要加PostMapping并處理重定向表單驗(yàn)證出錯(cuò)時(shí)用BindingResult把錯(cuò)誤信息回顯到頁(yè)面。你把這些點(diǎn)改過(guò)一遍源碼就算是真正讀懂了。5. 部署與答辯避坑高頻翻車現(xiàn)象、原因與排查方法5.1 數(shù)據(jù)庫(kù)連不上時(shí)區(qū)、編碼、未建庫(kù)三類報(bào)錯(cuò)現(xiàn)象一啟動(dòng)日志報(bào)Communications link failure或The server time zone value ?D1ú±ê×?ê±?? is unrecognized。原因MySQL 8.0 要求連接串顯式指定時(shí)區(qū)而配置里沒帶serverTimezone。解決在application.yml的 URL 末尾加上serverTimezoneAsia/Shanghai重啟?,F(xiàn)象二控制臺(tái)報(bào)Unknown database blog。原因SQL 文件沒導(dǎo)入或者導(dǎo)入時(shí)庫(kù)名和連接串不一致。解決按 3.2 的命令重新建庫(kù)導(dǎo)入然后確認(rèn)配置文件里的庫(kù)名和實(shí)際庫(kù)名一字不差。注意大小寫MySQL 在 Linux 下表名庫(kù)名是區(qū)分大小寫的?,F(xiàn)象三中文亂碼頁(yè)面上全是???。原因建庫(kù)時(shí)沒指定utf8mb4或者 JDBC URL 少了characterEncodingutf8。解決把庫(kù)、表、連接串三級(jí)全部統(tǒng)一成utf8mb4 / utf8改完重新啟動(dòng)并刷新頁(yè)面。這三個(gè)問題占了「數(shù)據(jù)庫(kù)連不上」九成以上任何一個(gè)都可以在答辯現(xiàn)場(chǎng)直接讓你翻車提前用上面的命令自查一遍。5.2 端口被占用和項(xiàng)目起不來(lái)現(xiàn)象啟動(dòng)日志有Port 8080 was already in use。原因之前運(yùn)行的后臺(tái)程序沒退干凈或者本地有其他服務(wù)占了 8080。排查命令# Windows 查看占用 8080 的進(jìn)程 netstat -ano | findstr 8080 taskkill /PID 你的進(jìn)程號(hào) /F # macOS / Linux lsof -i :8080 kill -9 你的進(jìn)程號(hào)殺掉進(jìn)程后重啟即可。另一個(gè)起不來(lái)的場(chǎng)景是java -jar啟動(dòng)后訪問 404原因多半是模板引擎頁(yè)面路徑不對(duì)——Thymeleaf 默認(rèn)去templates/下找 HTML如果你的頁(yè)面放到了static/下Controller 返回的視圖名永遠(yuǎn)找不到控制臺(tái)還會(huì)打一行Circular view path [index]的警告。解決是確認(rèn)視圖名和templates/下的文件名完全一致區(qū)分大小寫。5.3 打包后資源丟失和靜態(tài) 404現(xiàn)象IDEA 里運(yùn)行一切正常按 3.4 打包成 jar 后再訪問發(fā)現(xiàn) CSS、JS 全部 404。原因打包時(shí)資源文件沒被包含進(jìn) jar或者代碼里用了硬編碼路徑。解決打開pom.xml確認(rèn)build段里有resources配置把src/main/resources納入打包然后把頁(yè)面里的/css/xx.css全部改成th:href動(dòng)態(tài)寫法重新mvn clean package。這個(gè)坑幾乎人人都踩提前踩完部署演示時(shí)就多一分從容。還有個(gè)冷門但常發(fā)生的SQL 文件沒被包進(jìn) jar。如果你的初始化 SQL 放在resources/sql/下而 pom 沒包含**/*.sql部署到新機(jī)器上就會(huì)「有代碼沒數(shù)據(jù)」。解決是在resources配置里加一條include**/*.sql/include或者干脆把 SQL 文件和 jar 放在一起手動(dòng)導(dǎo)入。5.4 答辯演示前的四個(gè)準(zhǔn)備動(dòng)作演示翻車往往不是技術(shù)問題是流程問題。第一準(zhǔn)備一個(gè)專用演示賬號(hào)并確認(rèn)密碼正確不要現(xiàn)場(chǎng)輸密碼手一抖就完蛋。第二預(yù)置演示數(shù)據(jù)至少 5 篇文章、3 個(gè)分類、4 個(gè)標(biāo)簽、2 條評(píng)論別打開后臺(tái)發(fā)現(xiàn)是空庫(kù)。第三把log-impl的 SQL 日志留著演示時(shí)故意操作一次新增文章讓控制臺(tái)滾出INSERT INTO語(yǔ)句這是證明「你懂底層」的現(xiàn)場(chǎng)證據(jù)。第四斷網(wǎng)檢查——如果頁(yè)面里引用了外部 CDN 的圖標(biāo)或字體答辯教室沒網(wǎng)時(shí)頁(yè)面會(huì)很難看提前把靜態(tài)資源本地化。這四條做完心態(tài)上已經(jīng)贏了七成。5.5 初始賬號(hào)找不到模板最容易被忽略的坑現(xiàn)象數(shù)據(jù)庫(kù)導(dǎo)入成功但登錄頁(yè)面輸什么密碼都報(bào)錯(cuò)。原因SQL 文件里的初始用戶表數(shù)據(jù)有但密碼是加密串你輸?shù)?123456 明文自然匹配不上。解決找到項(xiàng)目里處理登錄的 Service看它用的是 BCrypt、MD5 還是別的算法如果是 BCrypt去在線生成器把 123456 加密后 UPDATE 進(jìn)用戶表如果是 MD5同樣生成密文后更新。這條我建議無(wú)論如何都提前做好因?yàn)樗恰缚雌饋?lái)全對(duì)但就是進(jìn)不去」的典型黑匣子問題也是我見過(guò)導(dǎo)致答辯現(xiàn)場(chǎng)直接斷片的頭號(hào)原因。6. 讓畢設(shè)從「能跑」到「能加分」三個(gè)進(jìn)階方向與一個(gè)驗(yàn)證方法基礎(chǔ)功能全通后你有余力就可以往加分項(xiàng)上走優(yōu)先級(jí)按投入產(chǎn)出比排。第一個(gè)是 Markdown 富文本編輯把文章 content 字段存 Markdown 源文展示時(shí)用 commonmark-java 庫(kù)渲染成 HTML改動(dòng)只涉及編輯頁(yè)和詳情頁(yè)兩個(gè)地方答辯卻能講出「編輯器層和數(shù)據(jù)展示層分離」的設(shè)計(jì)思想。第二個(gè)是全文搜索別自己用LIKE %keyword%硬扛數(shù)據(jù)量大了性能斷崖直接給文章表加 MySQL 全文索引用MATCH(title, content) AGAINST(關(guān)鍵詞)查詢一句話就能講清楚「數(shù)據(jù)庫(kù)級(jí)的全文檢索方案」。第三個(gè)是圖片上傳。給后臺(tái)加一個(gè)圖片上傳接口存儲(chǔ)到本地目錄再把訪問路徑映射成虛擬 URL。這個(gè)功能幾乎每個(gè)答辯老師都會(huì)問「你的圖片存哪」你能答出「服務(wù)器目錄 虛擬映射 數(shù)據(jù)庫(kù)存路徑」三層比答「存數(shù)據(jù)庫(kù)」高一截。驗(yàn)證方法用一條命令就能完成。啟動(dòng)服務(wù)后用 curl 連打三個(gè)接口驗(yàn)證核心鏈路curl -X POST http://localhost:8080/admin/doLogin -d usernameadminpassword123456 curl http://localhost:8080/article/list?pageNum1pageSize5 curl -X POST http://localhost:8080/admin/article/save -d title測(cè)試content內(nèi)容categoryId1第一條看登錄是否返回成功第二條看列表是否返回 JSON第三條寫一條測(cè)試數(shù)據(jù)后去數(shù)據(jù)庫(kù)里查SELECT * FROM t_article ORDER BY id DESC LIMIT 1;確認(rèn)落庫(kù)成功。這三步做完你的畢設(shè)從代碼、數(shù)據(jù)到部署就是一條完整的、經(jīng)得起驗(yàn)證的鏈路。我個(gè)人的習(xí)慣是每次改完代碼都跑一遍這三條命令確認(rèn)沒有「改一處崩三處」。希望幫到你。本文還有配套的精品資源點(diǎn)擊獲取