:Spring Boot 3.2教學實踐方案)
簡介本資源是一份面向計算機專業(yè)本科生與Java初學者的畢業(yè)設計類實踐文檔聚焦校園博客系統(tǒng)的設計與實現(xiàn)全過程解決高校師生知識分享、學習記錄與互動交流的實際需求。文檔以Word格式.docx完整呈現(xiàn)共1個文件大小4.91MB內容涵蓋技術選型JavaSpring BootMySQLTomcat、系統(tǒng)可行性分析、功能與業(yè)務流程梳理、模塊化功能結構設計、數(shù)據(jù)庫E-R圖及數(shù)據(jù)表字段設計、前后臺核心功能實現(xiàn)細節(jié)含管理員、博主、前臺首頁以及系統(tǒng)測試方案與結論結構嚴謹、章節(jié)完整具備直接參考與復用價值。目前已有62人學習下載適合用于課程設計參考、畢設開題與技術方案借鑒尤其利于理解Web應用從需求分析到部署落地的全周期開發(fā)邏輯。1. 為什么一個“校園博客系統(tǒng)”值得用 Java 重做一遍——不是為了炫技而是解決真實教學場景里的三類斷層你見過學生交的博客作業(yè)Word 文檔里貼截圖、手寫 URL、用郵箱附件傳.zip老師批改靠 Excel 打分表評論寫在 Word 批注里反饋周期動輒一周課程群每天刷屏“老師我的博客打不開”“數(shù)據(jù)庫連不上”“登錄后跳回首頁”沒人知道是前端路由錯、Session 失效還是 MySQL 8.0 默認認證插件不兼容。這不是開發(fā)能力問題是教學系統(tǒng)與工程實踐之間存在三道斷層需求斷層學生要交作業(yè)不是建 SaaS、部署斷層機房只裝 JDK 17 Tomcat 9不配 Docker、維護斷層畢設答辯完代碼就進回收站沒人管半年后的 Spring Boot 版本升級。這個“基于 Java 的校園博客系統(tǒng)”不是又一個 CRUD 演示項目它是把 Java 工程能力壓進教學閉環(huán)的最小可行載體用Spring Boot 3.2做骨架MyBatis-Plus 4.3自動生成建表 SQL 和基礎 CRUDThymeleaf 3.1渲染純 HTML 頁面避開 Vue/React 學習成本所有功能跑在單臺 Windows Server 上學生 clone 后改兩行配置就能本地啟動老師導出 WAR 包扔進機房 Tomcat 就能開課。它不追求高并發(fā)但必須扛住 200 人同時提交作業(yè)的瞬時壓力它不玩微服務但每個模塊用戶、文章、評論、權限都按真實項目分包它甚至預留了“教師后臺一鍵清空學生數(shù)據(jù)”的按鈕——因為每學期初真有老師需要重置整個班級的博客空間。如果你正帶 Java 課程設計、指導畢業(yè)設計或想給實訓平臺加個可交付的練手項目這個系統(tǒng)就是那塊“墊腳石”不高但剛好夠到工程落地的門檻。2. 從零搭起骨架用 Spring Boot 3.2 MyBatis-Plus 4.3 初始化項目并生成基礎表結構2.1 創(chuàng)建項目并確認 JDK 與依賴版本的硬性約束校園環(huán)境對運行時版本極其敏感。機房服務器普遍為 Windows Server 2016預裝 JDK 17LTS而部分老教師電腦仍用 JDK 11。Spring Boot 3.x 要求最低 JDK 17因此必須放棄 JDK 11 兼容性這是第一個技術取舍。創(chuàng)建項目時使用 start.spring.io 注意選 Spring Boot 3.2.x非 3.3勾選以下依賴Spring WebSpring Data JDBC非 JPA因 MyBatis-Plus 更適配教學場景的 SQL 可視化Lombok減少樣板代碼學生易理解Thymeleaf服務端渲染避免前端框架學習負擔Validation校驗學生注冊郵箱、密碼格式提示不要勾選 Spring Security。校園系統(tǒng)需“教師賬號擁有全站權限學生僅能管理自己博客”用自定義攔截器比 Security 配置更直觀也方便學生 debug 權限邏輯。生成 ZIP 后解壓用 IDEA 打開。關鍵一步檢查pom.xml中 MyBatis-Plus 版本是否為4.3.02023 年底發(fā)布支持 Spring Boot 3.2且TableName注解默認開啟駝峰轉下劃線省去手動配置。若版本不符強制覆蓋dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-spring-boot3-starter/artifactId version4.3.0/version /dependency2.2 用 MyBatis-Plus 逆向工程生成實體類與建表 SQL三步完成數(shù)據(jù)庫初始化學生最怕“建表語句寫錯導致整個項目崩”。MyBatis-Plus 的AutoGenerator可根據(jù) Java 實體類反向生成建表 SQL這才是教學友好型方案。我們先定義核心實體類以BlogPost為例// src/main/java/com/example/campusblog/entity/BlogPost.java import com.baomidou.mybatisplus.annotation.*; TableName(blog_post) // 顯式指定表名避免自動轉 blog_post public class BlogPost { TableId(type IdType.AUTO) // 主鍵自增MySQL 用 AUTO_INCREMENT private Long id; TableField(user_id) private Long userId; // 關聯(lián)學生 ID TableField(title) private String title; TableField(content) private String content; TableField(create_time) private LocalDateTime createTime; // 使用 LocalDateTime非 Date TableField(status) // 0-草稿, 1-已發(fā)布, 2-已刪除 private Integer status; // getter/setter 省略Lombok Data 已覆蓋 }關鍵點在于TableField顯式標注字段名禁止依賴駝峰自動轉換——因為學生常把createTime寫成create_time或createtime顯式聲明能杜絕歧義。接下來編寫代碼生成器放在src/test/java下僅用于初始化// src/test/java/com/example/campusblog/generator/CodeGenerator.java import com.baomidou.mybatisplus.generator.FastAutoGenerator; import com.baomidou.mybatisplus.generator.config.OutputFile; import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine; import java.util.Collections; public class CodeGenerator { public static void main(String[] args) { FastAutoGenerator.create(jdbc:mysql://localhost:3306/campus_blog?serverTimezoneAsia/ShanghaiuseSSLfalse, root, 123456) .globalConfig(builder - { builder.author(teacher) // 作者名生成代碼時寫入注釋 .outputDir(src/main/java); // 生成到 src/main/java }) .packageConfig(builder - { builder.parent(com.example.campusblog) // 包名前綴 .moduleName(entity) // 模塊名影響子包路徑 .pathInfo(Collections.singletonMap(OutputFile.xml, src/main/resources/mapper)); // XML 映射文件位置 }) .strategyConfig(builder - { builder.addInclude(blog_user, blog_post, blog_comment) // 只生成這三張表 .entityBuilder() .enableLombok() // 啟用 Lombok .naming(com.baomidou.mybatisplus.generator.config.NamingStrategy.underline_to_camel) // 數(shù)據(jù)庫下劃線轉駝峰 .controllerBuilder() .enableRestStyle(); // 生成 RESTful 風格 Controller }) .templateEngine(new FreemarkerTemplateEngine()) // 使用 Freemarker 模板 .execute(); } }執(zhí)行main()方法后自動生成BlogUser.java,BlogPost.java,BlogComment.java實體類BlogUserMapper.java,BlogPostMapper.java接口含insert,selectById等方法BlogUserMapper.xml,BlogPostMapper.xml含insertselect標簽BlogUserController.java,BlogPostController.java帶RestController的空殼參數(shù)說明serverTimezoneAsia/Shanghai是必須項否則LocalDateTime字段插入 MySQL 會報時區(qū)錯誤useSSLfalse因校園內網(wǎng) MySQL 通常未配 SSL強行開啟會導致連接失敗addInclude明確限定表名避免生成無關表如flyway_schema_history。2.3 運行生成器后獲取建表 SQL 并手動執(zhí)行為什么不能直接schema.sqlMyBatis-Plus 不提供“一鍵建庫建表”功能但生成器輸出的 XML 文件中insert標簽內的 SQL 是可讀的。更可靠的做法是用 HikariCP 連接池 spring.sql.init.modealways加載初始化腳本。在src/main/resources/application.yml中添加spring: sql: init: mode: always schema-locations: classpath:schema.sql >-- src/main/resources/schema.sql CREATE TABLE IF NOT EXISTS blog_user ( id BIGINT AUTO_INCREMENT PRIMARY KEY, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(100) NOT NULL, email VARCHAR(100) NOT NULL, role TINYINT DEFAULT 0 COMMENT 0-學生, 1-教師, create_time DATETIME DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE IF NOT EXISTS blog_post ( id BIGINT AUTO_INCREMENT PRIMARY KEY, user_id BIGINT NOT NULL, title VARCHAR(200) NOT NULL, content TEXT NOT NULL, status TINYINT DEFAULT 1 COMMENT 0-草稿,1-已發(fā)布,2-已刪除, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, INDEX idx_user_id (user_id) ); CREATE TABLE IF NOT EXISTS blog_comment ( id BIGINT AUTO_INCREMENT PRIMARY KEY, post_id BIGINT NOT NULL, user_id BIGINT NOT NULL, content VARCHAR(500) NOT NULL, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, INDEX idx_post_id (post_id) );為什么不用MapperScan自動建表因為MapperScan只掃描 Mapper 接口不執(zhí)行 DDL。而schema.sql在應用啟動時由 Spring JDBC 自動執(zhí)行且IF NOT EXISTS保證重復啟動不報錯——這對學生反復重啟項目至關重要。3. 讓學生真正“交作業(yè)”實現(xiàn)博客發(fā)布流程與教師后臺審核鏈路3.1 學生端從登錄到發(fā)布三步完成一篇博客含富文本與圖片上傳校園博客不需復雜編輯器。用summernote輕量級 jQuery 富文本即可它支持圖片拖拽上傳且 JS 文件僅 300KB。在src/main/resources/templates/user/dashboard.html中引入!-- src/main/resources/templates/user/dashboard.html -- link hrefhttps://cdn.jsdelivr.net/npm/summernote0.8.18/dist/summernote.min.css relstylesheet script srchttps://cdn.jsdelivr.net/npm/summernote0.8.18/dist/summernote.min.js/script div classform-group label博客內容/label textarea idcontent-editor namecontent classform-control/textarea /div script $(document).ready(function() { $(#content-editor).summernote({ height: 300, toolbar: [ [style, [bold, italic, underline]], [insert, [picture, link]] ], callbacks: { onImageUpload: function(files) { sendFile(files[0]); // 調用自定義上傳函數(shù) } } }); }); function sendFile(file) { var formData new FormData(); formData.append(file, file); $.ajax({ url: /api/upload/image, type: POST, data: formData, processData: false, contentType: false, success: function(url) { $(#content-editor).summernote(insertImage, url); // 插入圖片 URL } }); } /script后端接收圖片的 Controller 必須處理MultipartFile并保存到src/main/resources/static/upload/目錄靜態(tài)資源目錄圖片可直接通過 HTTP 訪問// src/main/java/com/example/campusblog/controller/UploadController.java import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; import org.springframework.core.io.Resource; import org.springframework.core.io.UrlResource; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; RestController RequestMapping(/api/upload) public class UploadController { private final Path rootLocation Paths.get(src/main/resources/static/upload); PostMapping(/image) public String handleFileUpload(RequestParam(file) MultipartFile file) { try { if (file.isEmpty()) { return ; } // 生成唯一文件名時間戳 原文件名 String timestamp LocalDateTime.now().format(DateTimeFormatter.ofPattern(yyyyMMddHHmmss)); String filename timestamp _ file.getOriginalFilename(); Path destinationFile this.rootLocation.resolve( Paths.get(filename)) .normalize().toAbsolutePath(); // 確保目錄存在 if (!this.rootLocation.toFile().exists()) { Files.createDirectories(this.rootLocation); } Files.copy(file.getInputStream(), destinationFile); // 返回可訪問的 URL注意static 目錄下文件路徑為 /upload/xxx.jpg return /upload/ filename; } catch (Exception e) { e.printStackTrace(); return ; } } }關鍵細節(jié)return /upload/ filename是硬編碼路徑因為src/main/resources/static/是 Spring Boot 默認靜態(tài)資源目錄無需額外配置即可通過http://localhost:8080/upload/xxx.jpg訪問。學生寫博客時插入的圖片 URL 就是這個地址避免使用file://協(xié)議瀏覽器安全限制或絕對路徑部署后失效。3.2 教師端審核列表與一鍵操作用 Thymeleaf 渲染動態(tài)表格教師后臺首頁/admin/posts展示待審核博客列表。MyBatis-Plus 分頁查詢 Thymeleaf 表格渲染代碼簡潔且易調試// src/main/java/com/example/campusblog/controller/AdminController.java GetMapping(/admin/posts) public String listPendingPosts(Model model) { PageBlogPost page new Page(1, 10); // 第 1 頁每頁 10 條 QueryWrapperBlogPost wrapper new QueryWrapper(); wrapper.eq(status, 0); // 只查草稿狀態(tài) wrapper.orderByDesc(create_time); PageBlogPost resultPage blogPostService.page(page, wrapper); model.addAttribute(posts, resultPage.getRecords()); model.addAttribute(page, resultPage); return admin/post-list; // 對應 templates/admin/post-list.html }post-list.html中用 Thymeleaf 渲染表格!-- src/main/resources/templates/admin/post-list.html -- table classtable table-striped thead tr th標題/th th學生/th th提交時間/th th操作/th /tr /thead tbody tr th:eachpost : ${posts} td th:text${post.title}示例標題/td td th:text${post.user.username}張三/td td th:text${#dates.format(post.createTime, yyyy-MM-dd HH:mm)}2024-05-01 10:20/td td a th:href{/admin/post/{id}/publish(id${post.id})} classbtn btn-success btn-sm發(fā)布/a a th:href{/admin/post/{id}/reject(id${post.id})} classbtn btn-danger btn-sm退回/a /td /tr /tbody /table發(fā)布操作只需更新status字段// src/main/java/com/example/campusblog/controller/AdminController.java PostMapping(/admin/post/{id}/publish) public String publishPost(PathVariable Long id) { BlogPost post blogPostService.getById(id); if (post ! null post.getStatus() 0) { post.setStatus(1); // 1-已發(fā)布 blogPostService.updateById(post); } return redirect:/admin/posts; }為什么不用 REST API AJAX因為教師可能用老舊 IE 瀏覽器機房常見AJAX 兼容性差而redirect是最穩(wěn)妥的頁面跳轉方式且 Thymeleaf 模板天然支持服務端渲染學生 debug 時直接看 HTML 源碼就能理解數(shù)據(jù)流向。3.3 權限控制用攔截器實現(xiàn)“學生只能看自己的博客教師看全部”不引入 Spring Security改用HandlerInterceptor實現(xiàn)細粒度控制。創(chuàng)建AuthInterceptor// src/main/java/com/example/campusblog/interceptor/AuthInterceptor.java import org.springframework.web.servlet.HandlerInterceptor; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; public class AuthInterceptor implements HandlerInterceptor { Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { HttpSession session request.getSession(false); if (session null || session.getAttribute(user) null) { response.sendRedirect(request.getContextPath() /login); return false; } String uri request.getRequestURI(); BlogUser user (BlogUser) session.getAttribute(user); // 學生不能訪問 /admin/ 開頭的路徑 if (uri.startsWith(/admin/) user.getRole() 0) { response.sendError(HttpServletResponse.SC_FORBIDDEN, 無權限訪問管理員頁面); return false; } // 學生訪問 /post/{id} 時校驗該博客是否屬于本人 if (uri.matches(/post/\\d) user.getRole() 0) { Long postId Long.parseLong(uri.substring(uri.lastIndexOf(/) 1)); BlogPost post blogPostService.getById(postId); if (post null || !post.getUserId().equals(user.getId())) { response.sendError(HttpServletResponse.SC_FORBIDDEN, 無權查看他人博客); return false; } } return true; } }在WebMvcConfigurer中注冊// src/main/java/com/example/campusblog/config/WebConfig.java import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; Configuration public class WebConfig implements WebMvcConfigurer { Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new AuthInterceptor()) .excludePathPatterns(/login, /register, /css/**, /js/**, /images/**); } }攔截器 vs FilterFilter 在 Servlet 容器層面無法直接獲取 Spring Bean如blogPostService而HandlerInterceptor在 Spring MVC 攔截鏈中可注入 Service適合業(yè)務邏輯判斷。這是教學場景下的合理降級——用更易懂的機制替代復雜框架。4. 避坑指南學生部署時最常遇到的 4 個致命問題與血淚解決方案4.1 現(xiàn)象本地啟動成功但部署到機房 Tomcat 后頁面空白控制臺無報錯原因Spring Boot 3.x 默認打包為jar而機房 Tomcat 要求war包且jar包內嵌 Tomcat 與外部 Tomcat 沖突。解決修改pom.xml將打包方式改為warpackagingwar/packaging排除內嵌 Tomcatdependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-tomcat/artifactId scopeprovided/scope !-- 交給外部 Tomcat 提供 -- /dependency主類繼承SpringBootServletInitializerSpringBootApplication public class CampusBlogApplication extends SpringBootServletInitializer { Override protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { return application.sources(CampusBlogApplication.class); } public static void main(String[] args) { SpringApplication.run(CampusBlogApplication.class, args); } }用mvn clean package -DskipTests生成target/campus-blog-0.0.1-SNAPSHOT.war丟進Tomcat/webapps/即可。4.2 現(xiàn)象學生注冊時提示“郵箱格式錯誤”但輸入studentschool.edu.cn仍失敗原因Email注解默認正則過于嚴格不支持中文域名或長后綴如.edu.cn。解決自定義校驗注解或改用Pattern// 在 BlogUser 實體類中 Pattern(regexp ^[a-zA-Z0-9._%-][a-zA-Z0-9.-]\\.[a-zA-Z]{2,}$, message 郵箱格式不正確請輸入如 studentschool.edu.cn) private String email;玄學經(jīng)驗正則末尾{2,}允許任意長度頂級域名比Email的硬編碼列表更包容校園郵箱。4.3 現(xiàn)象教師后臺點擊“發(fā)布”后博客狀態(tài)沒變刷新頁面仍是草稿原因blogPostService.updateById(post)更新時MyBatis-Plus 默認只更新非null字段而post對象中userId,title等字段為null因只設置了id和status導致 SQL 中SET status?被忽略。解決兩種方案任選其一方案 A推薦用LambdaUpdateWrapper顯式指定更新字段LambdaUpdateWrapperBlogPost wrapper new LambdaUpdateWrapper(); wrapper.eq(BlogPost::getId, id).set(BlogPost::getStatus, 1); blogPostService.update(wrapper);方案 B在BlogPost實體類中為所有字段添加TableField(fill FieldFill.UPDATE)并在MetaObjectHandler中填充但教學場景過于復雜不推薦。4.4 現(xiàn)象圖片上傳后顯示 404URL 是/upload/20240501102030_xxx.jpg但瀏覽器打不開原因src/main/resources/static/upload/目錄在打包成war后路徑變?yōu)閃EB-INF/classes/static/upload/而 Tomcat 默認不將WEB-INF下的資源設為靜態(tài)可訪問。解決在application.yml中顯式配置靜態(tài)資源路徑spring: web: resources: static-locations: classpath:/static/,file:./upload/然后修改上傳代碼將圖片保存到項目根目錄的upload/文件夾而非src/main/resources/static/upload/private final Path rootLocation Paths.get(./upload); // 改為相對路徑這樣./upload/位于 Tomcat 的webapps/ROOT/同級天然可被 HTTP 訪問。5. 讓系統(tǒng)“活”過一個學期教師后臺的批量操作與數(shù)據(jù)快照技巧5.1 教師一鍵清空全班博客用 MyBatis-Plus 的remove方法安全擦除每學期初教師需要重置學生博客空間。與其手動刪庫不如在后臺加個按鈕。在AdminController中添加PostMapping(/admin/clear-all-posts) public String clearAllPosts() { // 刪除所有博客軟刪除status2 UpdateWrapperBlogPost wrapper new UpdateWrapper(); wrapper.set(status, 2) // 標記為已刪除 .ne(status, 2); // 避免重復操作 blogPostService.update(wrapper); // 清空評論 UpdateWrapperBlogComment commentWrapper new UpdateWrapper(); commentWrapper.set(status, 2) .ne(status, 2); blogCommentService.update(commentWrapper); return redirect:/admin/dashboard; }對應前端按鈕!-- 在 admin/dashboard.html 中 -- form action/admin/clear-all-posts methodpost button typesubmit classbtn btn-danger onclickreturn confirm(確定清空所有博客和評論此操作不可撤銷) 清空全班博客 /button /form為什么用軟刪除status2而非物理刪除因為教師可能誤點物理刪除后無法恢復而軟刪除只是標記狀態(tài)SQL 查詢時加WHERE status ! 2即可過濾且保留數(shù)據(jù)用于期末統(tǒng)計如“本學期共提交博客 1200 篇”。5.2 生成學期數(shù)據(jù)快照用SELECT ... INTO OUTFILE導出學生博客為 CSV期末歸檔時教師需要導出所有已發(fā)布博客為 Excel。MySQL 原生命令SELECT ... INTO OUTFILE可直接生成 CSV比 Java 導出更高效-- 在 MySQL 客戶端執(zhí)行需 FILE 權限 SELECT u.username AS 學生姓名, p.title AS 博客標題, p.content AS 博客內容, p.create_time AS 發(fā)布時間 INTO OUTFILE /var/lib/mysql-files/campus_blog_export.csv FIELDS TERMINATED BY , OPTIONALLY ENCLOSED BY LINES TERMINATED BY \n FROM blog_post p JOIN blog_user u ON p.user_id u.id WHERE p.status 1;注意/var/lib/mysql-files/是 MySQLsecure_file_priv參數(shù)指定的安全目錄不能隨意改路徑導出后教師用scp或遠程桌面復制該文件到本地用 Excel 打開即為標準 CSV。5.3 防止學生“刷分”用 Redis 記錄每日發(fā)布上限有學生為拿高分一天發(fā) 20 篇博客。在BlogPostController的發(fā)布接口中加入限流// src/main/java/com/example/campusblog/controller/BlogPostController.java Autowired private RedisTemplateString, Object redisTemplate; PostMapping(/post/publish) public String publishPost(RequestBody BlogPost post, HttpSession session) { BlogUser user (BlogUser) session.getAttribute(user); // 限流每天最多 3 篇 String key post_limit: user.getId() : LocalDate.now(); Long count redisTemplate.opsForValue().increment(key, 1); if (count 3) { model.addAttribute(error, 今日發(fā)布次數(shù)已達上限3 篇); return user/dashboard; } redisTemplate.expire(key, 1, TimeUnit.DAYS); // 設置 1 天過期 // 正常發(fā)布邏輯... post.setUserId(user.getId()); post.setStatus(0); // 先存草稿 blogPostService.save(post); return redirect:/user/dashboard; }Redis 部署建議校園機房可安裝 Windows 版 Redis微軟維護的redis-windows內存占用僅 10MB比數(shù)據(jù)庫計數(shù)更輕量。若無 Redis退化為數(shù)據(jù)庫計數(shù)SELECT COUNT(*) FROM blog_post WHERE user_id? AND DATE(create_time)CURDATE()但性能稍差。5.4 我的習慣每次課程更新前用 Git Tag 鎖定教學版本我?guī)У?Java 課程每年迭代但不同屆學生用的系統(tǒng)版本必須一致。我的做法是每學期初在 GitHub 倉庫打 Tag如v2024-spring給學生文檔明確寫“請 clonegit clone -b v2024-spring https://github.com/yourname/campus-blog.git”所有application.yml中的數(shù)據(jù)庫密碼、Redis 地址等敏感配置用application-dev.yml單獨存放并加入.gitignore最后把v2024-springTag 對應的 ZIP 包上傳到學校教學平臺供學生直接下載。這看似多一步卻避免了“學生用最新版代碼結果 Spring Boot 升級到 3.3JDK 17 不兼容”的翻車現(xiàn)場。工程能力的第一課不是寫多炫的算法而是讓代碼在別人機器上穩(wěn)穩(wěn)跑起來。希望幫到你。本文還有配套的精品資源點擊獲取