ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

SpringBoot校园社交平台:前后端分离与工程化实践全解析

SpringBoot校园社交平台:前后端分离与工程化实践全解析 简介基于SpringBoot构建的校园社交平台毕设项目运行环境涵盖JDK1.8、Tomcat7与MySQL5.7包含完整可运行源码、配套说明文档和数据库文件面向计算机专业学生可用于毕业设计选题、课程设计或Java Web开发实践。资源包共717个文件涵盖Java源码、Vue前端、SVG/PNG图标、JPG图片、JS/CSS样式等前端资源以及XML配置文件、SQL数据库脚本、PDF/Word说明文档和多组bat批处理脚本压缩包约22.51MB目录结构清晰便于按模块检索。已有52人学习下载。项目中admin与front目录分别对应后台管理和前端页面内置的自动化脚本可一键完成依赖安装、启动运行与项目构建减少环境部署成本。通过源码和配套文档可系统学习SpringBoot框架、Maven依赖管理、前后端分离开发、MySQL数据库表设计及接口开发方法配合SQL脚本可快速完成数据库初始化为独立完成校园社交类应用打下坚实基础。1. 校园社交平台选型拿到的不只是一份 CRUD校园社交平台在毕业设计里出现频率很高但真正拉开差距的往往不是发帖、评论这些表层功能而是数据模型怎么设计、权限怎么收敛、以及前后端分离之后谁来托管前端产物。手头这份基于 SpringBoot 的校园社交平台项目源码目录里admin和front并存install.bat、build.bat、run.bat三件套齐全说明它不是一套把页面塞进templates的传统单体而是走了一套“前端独立构建、后端统一托管”的工程化路线。适合两类人一类是拿它当毕设起点想快速跑通“用户端 管理后台 MySQL 5.7”完整闭环的同学另一类是已经写过 CRUD想看看 SpringBoot 项目里静态资源映射、跨域收敛、依赖脚本这些工程细节怎么做的人。下面从目录结构开始拆按“骨架 → 数据 → 集成 → 验证”的顺序把整条链路讲透。2. 项目骨架解码admin 与 front 双端结构下的启动链路2.1 从目录结构看懂前后端职责边界解压后最值得先看的是src/main/resources下的admin与front两个目录。前者是管理后台的前端工程后者是用户端的前端工程。两个目录里都出现了install.bat、build.bat、run.bat依赖管理方式一眼就能认出来——这是 Node 生态的标准脚本命名。install.bat对应依赖安装build.bat对应生产构建run.bat对应本地开发服务器。src/main/resources ├── admin # 管理后台前端工程 │ └── admin │ ├── dist # 构建产物 │ │ └── css │ │ └── app.debe4542.css │ ├── 1-install.bat # 安装 npm 依赖 │ ├── 2-run.bat # 启动开发服务器 │ └── 3-build.bat # 打包到 dist └── front # 用户端前端工程 └── front ├── install.bat ├── run.bat └── build.batapp.debe4542.css这种带 hash 的文件名说明dist目录里是已经过 webpack 或 vite 打包的产物。也就是说前端代码已经完成了压缩、指纹标记和路径重写后端不需要安装 Node 环境就能直接托管这些静态文件。这个设计对毕设部署很友好只要把dist放进 classpathSpringBoot 内置容器就会把它当作静态资源对外提供服务。这三个脚本的职责必须分清很多同学在2-run.bat里起了一个前端开发服务器比如 8081 端口又在 IDE 里启动了 SpringBoot8080 端口结果页面请求接口时被浏览器跨域策略拦下来卡了半天。注意run.bat是给前端开发调试用的而最终交给老师验收的形态是build.bat产出的 dist 目录由 SpringBoot 统一托管。我一般建议刚拿到项目时先跑install.bat装依赖然后直接跑build.bat用打包产物配合后端联调避免双端口跨域问题。2.2 Maven 与 JDK 版本约束先对齐再谈启动摘要里明确写了 JDK 1.8、tomcat7、MySQL 5.7 这一套环境组合。SpringBoot 2.x 系列是基于 JDK 1.8 的主流选择如果从网上下载的脚手架是 Spring Boot 3.x默认要求 JDK 17直接迁移到这套环境里会报UnsupportedClassVersionError或者启动失败。拿到项目后第一步不是看代码而是打开pom.xml确认 SpringBoot 父依赖版本再看java.version属性是不是 1.8。parent groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-parent/artifactId version2.x.x/version relativePath/ /parent properties java.version1.8/java.version /properties提示spring-boot-starter-parent里的 version 决定了内嵌 Tomcat 的版本。SpringBoot 2.x 默认内嵌 Tomcat 9即使外置部署到 tomcat7代码里也不建议使用 Tomcat 9 才有的 API。另外 MySQL 驱动版本要和数据库版本匹配5.7 数据库使用mysql-connector-java8.0.x 也能连接但driver-class-name必须写成com.mysql.cj.jdbc.Driver并在 URL 后面追加serverTimezoneAsia/Shanghai否则会报时区错误。如果本地只装了新版 JDK又不想换版本可以在 IDEA 的 Project Structure 里给这个模块单独指定 1.8 的 SDK同时确认 Maven 的JAVA_HOME环境变量指向 JDK 1.8 安装目录。开发软件层面eclipse、myeclipse、idea 都能导入但 idea 对 Maven 多模块和前端资源目录的支持最省心建议直接用 idea。2.3 SpringBoot 如何托管前端构建产物前后端分离的工程常见做法是后端把前端的 dist 目录复制到src/main/resources/staticSpringBoot 启动时会自动将这个目录映射到根路径/。如果不想手动复制也可以在安装脚本里加入一条xcopy命令把 dist 里的内容同步到 static让整个构建流程串起来echo off REM 前端构建完成后将产物同步到 SpringBoot 静态资源目录 xcopy /E /Y dist ..\..\..\static\admin\ echo admin frontend built and copied to static/这里xcopy的/E表示复制所有子目录包括空目录/Y表示覆盖时不提示。路径..\..\..\static\admin\是相对于 bat 文件所在目录的推算值具体深度取决于你的admin目录层级实际操作时建议先打印一次当前路径确认相对路径没有走偏。对于更复杂的路由场景比如前端用了 vue-router 的 history 模式刷新/post/123这类路径时 SpringBoot 会返回 404。此时需要在后端加一个路由兜底把所有非 API 请求转发到 index.htmlConfiguration public class WebMvcConfig implements WebMvcConfigurer { Override public void addViewControllers(ViewControllerRegistry registry) { registry.addViewController(/{path:[^\\.]*}) .setViewName(forward:/index.html); } }这段配置的含义是匹配所有不带点号的路径[^\\.]*把它们转发到index.html由前端路由接管。addViewController注册了一个视图控制器forward是服务端转发浏览器地址栏不变但内容换成了前端入口文件。这样就避免了刷新页面时出现 404。3. 数据层设计MySQL 5.7 下的核心表结构与查询优化3.1 五张核心表从用户到关注的关系建模校园社交平台的基本业务域可以收敛为五个实体用户、帖子、评论、点赞、关注。很多毕设项目把点赞数和关注数直接冗余在用户表或帖子表里理由是查询快但带来的一致性问题也不小用户取消点赞时如果只删记录不更新计数数字就漂移了。更稳妥的做法是保留明细表用统计查询或事务内更新计数来保证一致这在面试和答辩时能讲出设计依据。以点赞表为例明细表的设计要点是唯一索引去重和联合索引覆盖查询CREATE TABLE t_like ( id bigint(20) NOT NULL AUTO_INCREMENT, user_id bigint(20) NOT NULL COMMENT 点赞用户ID, topic_id bigint(20) NOT NULL COMMENT 帖子ID, create_time datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_user_topic (user_id, topic_id), KEY idx_topic_id (topic_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT点赞明细表;uk_user_topic这个联合唯一索引保证同一个用户对同一篇帖子只能点赞一次从数据库层面挡住了重复数据。idx_topic_id是给“查询某条帖子有多少人点赞”和“查询我赞过哪些帖子”这两个高频场景用的。InnoDB的事务和行锁特性支持后续的计数更新操作utf8mb4是为了兼容 emoji 表情——校园社交平台里用户昵称和帖子内容出现 emoji 的概率很高。3.2 评论计数与子查询的坑评论区常见的需求是“查帖子列表时带上评论数”。最简单的方式是在帖子表维护comment_count字段每次插入评论时加一但如果要严格准确可以用子查询实时计算。MySQL 5.7 对子查询的优化不算激进直接写select count(*) from t_comment where topic_id ?在大数据量下容易出现性能波动尤其是当这个子查询出现在列表页的循环里时会产生典型的 N1 查询问题。实际项目中我倾向于这样处理列表页用冗余字段展示评论数详情页用一条聚合查询验证数值是否一致。SELECT t.id, t.title, t.content, t.comment_count AS display_count, (SELECT COUNT(*) FROM t_comment c WHERE c.topic_id t.id) AS real_count FROM t_topic t WHERE t.status 1 ORDER BY t.create_time DESC LIMIT 10;display_count是冗余列real_count是实时计算列两者对比可以帮助检查冗余数据是否同步。LIMIT 10限制了扫描范围配合t.status 1的下推条件MySQL 可以使用到idx_status_time这类联合索引。这里有个细节ORDER BY t.create_time DESC和WHERE t.status 1需要联合索引才能避免 filesort如果只给create_time建单列索引数据量上来后排序性能会明显下降。3.3 关注关系与分页查询的索引选择关注表的结构更简单但查询模式更复杂。既要查“我关注了谁”也要查“谁关注了我”还要查“我和他是否互关”。推荐把关注关系设计成一行记录两个方向而不是拆成两张表CREATE TABLE t_follow ( id bigint(20) NOT NULL AUTO_INCREMENT, user_id bigint(20) NOT NULL COMMENT 关注者, follow_user_id bigint(20) NOT NULL COMMENT 被关注者, create_time datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_user_follow (user_id, follow_user_id), KEY idx_follow_user (follow_user_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT关注关系表;uk_user_follow防止重复关注idx_follow_user支撑“粉丝列表”查询。查互关状态时只需要在业务代码里发两条查询或者用一条IN加AND的组合。关注列表分页时ORDER BY create_time DESC会走idx_follow_user索引但create_time不在索引列里MySQL 需要回表排序。要消除这部分开销可以把索引改成(follow_user_id, create_time)这样索引本身就是有序的分页查询不需要额外 filesort。这套数据模型还有一个面试常考点为什么用bigint做主键而不用uuid。因为 InnoDB 聚簇索引按主键顺序组织数据自增bigint保证插入顺序和索引顺序一致不会像 uuid 那样造成页分裂和碎片化。校园社交平台的数据量虽然不会大到需要分库分表但表结构设计体现的是工程规范这一点在答辩时是加分项。4. 前后端联调从跨域收敛到登录态复用4.1 让前端开发服务器代理后端接口开发阶段使用run.bat启动前端时页面运行在 Node 开发的端口上比如 8081而 SpringBoot 跑在 8080必须配置开发代理才能让请求穿透到后端。前端工程里如果是 webpack配置文件是vue.config.js里的devServer.proxy如果是 vite配置文件是vite.config.js里的server.proxy。下面以常见的 vue.config.js 为例module.exports { devServer: { port: 8081, proxy: { /api: { target: http://localhost:8080, changeOrigin: true, pathRewrite: { ^/api: } } } } };这段配置的含义是前端开发服务器收到以/api开头的请求时把它转发到http://localhost:8080同时把路径开头的/api去掉。changeOrigin: true表示改写请求头里的Host字段为目标地址避免后端反向代理或虚拟主机判断出错。pathRewrite里的^/api: 是正则替换比如前端请求/api/login代理转发给后端时变成/login。如果后端的所有接口本来就带/api前缀就不需要这个 rewrite。判断标准很简单——看 SpringBoot 控制器里RequestMapping写的是/api/user/login还是/user/login两者对应不同的 rewrite 规则。4.2 后端 CORS 配置生产环境的收敛策略如果前端打包后直接由 SpringBoot 托管同源状态下不存在跨域问题但如果把前端 dist 放到 nginx 上就会出现跨域请求。有些毕设项目为了省事在后端配置了allowedOrigins(*)这等于允许任意站点调用你的接口安全隐患很大答辩时如果被问到“如何防止 CSRF”会很难圆。更合理的做法是把允许的来源收敛成明确的域名或端口列表Configuration public class CorsConfig implements WebMvcConfigurer { Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping(/api/**) .allowedOrigins(http://localhost:8081, http://localhost:80) .allowedMethods(GET, POST, PUT, DELETE, OPTIONS) .allowedHeaders(*) .allowCredentials(true) .maxAge(3600); } }addMapping(/api/**)只对接口路径生效静态资源不参与跨域判断。allowedOrigins列出的两个地址分别对应前端开发服务器和 nginx 部署地址。maxAge(3600)表示预检请求的结果缓存 3600 秒减少浏览器重复发送 OPTIONS 请求。注意一旦设置allowCredentials(true)allowedOrigins就不能写*必须明确指定来源这是浏览器的安全限制。4.3 登录态设计JWT 与拦截器的最小实现社交平台必然要区分登录用户和游客典型的毕设方案是基于 JWT 的无状态登录。用户提交账号密码后后端校验通过生成一个 token 返回给前端前端把它存到 localStorage 或 sessionStorage后续每次请求在请求头里带上Authorization: Bearer token。后端用一个拦截器统一校验Component public class JwtInterceptor implements HandlerInterceptor { Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { if (OPTIONS.equalsIgnoreCase(request.getMethod())) { return true; } String token request.getHeader(Authorization); if (token ! null token.startsWith(Bearer )) { String jwtToken token.substring(7); // 解析 token验证签名和过期时间 Claims claims Jwts.parser() .setSigningKey(secretKey) .parseClaimsJws(jwtToken) .getBody(); request.setAttribute(userId, claims.get(userId)); return true; } response.setStatus(401); return false; } }这段拦截器的逻辑分四步第一步放行 OPTIONS 预检请求否则跨域场景下浏览器预检会直接失败第二步从请求头取Authorization并校验前缀是否为Bearer第三步用签名的secretKey解析 token解析失败会抛出异常异常捕获后返回 401第四步把 token 里的userId存到 request 属性里后续 Controller 里直接拿request.getAttribute(userId)就能知道当前登录用户是谁。提示secretKey不要硬编码在代码里放到application.yml的custom.jwt.secret配置项中用Value注入。答辩时提到“密钥可配置化”比写死在类里更能体现工程意识。4.4 MySQL 连接配置与常见启动失败点数据库连接配置集中在src/main/resources/application.yml。MySQL 5.7 默认的认证插件是mysql_native_password如果 MySQL 8.0 安装了但项目要求 5.7登录和连接都会不兼容。配置模板如下spring: datasource: url: jdbc:mysql://localhost:3306/campus_social?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT8useUnicodetruecharacterEncodingutf8保证中文写入和读取不乱码serverTimezoneAsia/Shanghai解决 8.0 驱动对时区的强制校验。jackson配置控制接口返回 JSON 里的时间格式否则前端拿到的是时间戳数字还需要额外转换。如果启动报Unknown database说明campus_social这个库还没创建如果报Access denied优先检查用户名密码是否匹配再看 MySQL 服务的端口是不是 3306。5. 答辩前必做的验证清单与接口排障技巧5.1 用批处理脚本重演完整部署流程验收时最担心的是换一台机器就起不来。把整个流程固化成命令从零开始走一遍能暴露绝大多数环境问题。顺序是先装前端依赖、构建产物、同步到 static再启动 MySQL 并导入 SQL 脚本最后运行 SpringBoot。cd src/main/resources/front/front call install.bat cd src/main/resources/admin/admin call 1-install.bat call 3-build.bat cd ../.. mvn clean package -DskipTests java -jar target/campus-social-0.0.1-SNAPSHOT.jar第二条命令里的call是 Windows 批处理下调用另一个 bat 的写法直接写install.bat会中断当前脚本执行。-DskipTests跳过测试编译避免因为测试类引用了本地依赖导致打包失败。最后一行java -jar启动后看到Started Application in xx seconds才算成功。如果启动一闪而过用javaw换成java在前台运行错误信息会直接打在控制台里。5.2 热点接口的响应验证准备一张表格把核心接口、请求方式和预期结果整理出来逐条验证。这一步不仅是为了自己测试也是答辩时演示功能的脚本。接口路径方法参数预期结果/api/loginPOSTusername,password返回带 token 的 JSON/api/topic/listGETpageNum,pageSize返回帖子的分页列表/api/topic/detailGETid返回帖子详情与评论列表/api/like/addPOSTtopicId返回当前点赞总数/api/follow/addPOSTfollowUserId返回关注关系状态用 curl 模拟一次带 token 的请求可以直观看到结果curl -X POST http://localhost:8080/api/login \ -H Content-Type: application/json \ -d {username:admin,password:123456}拿到返回的 token 后把 token 拼到列表接口上curl -X GET http://localhost:8080/api/topic/list?pageNum1pageSize5 \ -H Authorization: Bearer eyJhbGciOiJIUzI1NiJ9...-H Authorization: Bearer ...是 JWT 的固定标准格式注意 Bearer 和 token 之间有一个空格。如果返回结果带有完整的字段不为null或正确分页数据说明接口链路通畅。如果返回 401优先检查 token 是否过期返回 500 则看后端控制台的异常堆栈最常见的两个错误是 MyBatis 映射 SQL 写错和 Jackson 序列化循环引用。5.3 用 EXPLAIN 检查慢 SQL 是否走了索引答辩时被问到“系统如何保证性能”最有力的回答是现场执行一次 EXPLAIN。以查帖子列表为例EXPLAIN SELECT id, title, content FROM t_topic WHERE status 1 ORDER BY create_time DESC LIMIT 10;观察type字段如果是ALL说明全表扫描数据量一旦上万就会变慢如果是ref或range说明用上了索引。Extra列如果出现Using filesort说明排序没有用上索引需要检查联合索引是否包含了create_time。优化方案是把索引设计成(status, create_time)联合索引这样WHERE status 1过滤和ORDER BY create_time DESC排序都能由同一个索引完成避免排序时的临时表开销。5.4 国际化时间陷阱与 MySQL 8.0 迁移最后提醒一个很容易踩的坑MySQL 5.7 的datetime默认精度是秒级SpringBoot 应用里使用LocalDateTime时两端时间基准必须一致。如果 MySQL 服务器时区是SYSTEM而 JVM 时区是GMT8写入的帖子时间可能比实际时间少 8 小时。建议启动 MySQL 时显式指定--default-time-zone8:00或者在连接 URL 里明确serverTimezoneGMT%2B8URL 里的号需要编码成%2B。如果后续想将数据库从 MySQL 5.7 爬到 8.0注意两点驱动版本必须升级到 8.0.x5.7 导出的 SQL 文件中utf8mb4_unicode_ci之类的排序规则在 8.0 里仍然兼容但ENGINEMyISAM的旧表需要主动转成 InnoDB。在配置好上述检查项后用 Postman 或 Apifox 跑一遍接口自动化集这套基于 SpringBoot 的校园社交平台就算真正立住了可以经得起现场演示和追问。本文还有配套的精品资源点击获取
返回列表