Spring Boot交通线路查询系统毕设实战:从换乘算法到Redis缓存优化
2026/9/18 23:03:30 网站建设 项目流程

交通线路查询系统这个毕设题,我在带学生做项目时遇到的频率相当高。它听起来不复杂,但真正动手以后你会发现,这个题目把 Spring Boot 的核心用法基本都串起来了——数据建模、ORM 框架操作、接口设计、算法实现、缓存处理、前后端联调,一样都少不了。曾有个学生跟我说,做完这个系统再去面试 Java 岗,被问到的 Spring Boot 问题大半都能答上几句,原因很简单,项目里踩过的坑就是最好的面试素材。

这篇文章就围绕“springboot 交通线路查询系统”这个题目,把从选题定位、技术选型、数据库设计到核心功能实现、常见问题排查的完整思路写清楚,里面也包含了不少只有实际做一遍才能发现的细节。无论是准备做这个题目的毕业生,还是想自己练手写一个查询系统的初学者,都值得从头到尾看一遍。

1. 项目整体设计与思路拆解

1.1 这个题目到底在做什么

交通线路查询系统,简单说就是让用户输入起点和终点,系统返回可行的交通线路、换乘方案、预计站点数量等信息。看起来像高德地图的一个小功能,但作为毕业设计,它真正考察的是你对“数据建模”和“业务逻辑”的组织能力。

我从实际带项目的经验出发,把功能边界拆成三个层次:

  • 基础层:站点管理、线路管理、站点与线路的关联关系维护,这部分是后台管理员做的。
  • 核心层:线路查询、站点查询、换乘方案推荐,这是面向普通用户的主要功能。
  • 扩展层:用户注册登录、收藏常用线路、查询历史记录、公告发布。

很多学生一上来就想做得很宏大,甚至想把实时公交、地图轨迹都塞进去,我基本都会劝住。一个合格的毕业设计,功能在精不在多,把线路查询这一个核心业务做深、做透,比堆砌一堆没人用的功能有价值得多。答辩时老师更关心的是:你的换乘策略是怎么设计的?数据结构是怎么组织的?这些想清楚了,系统就已经成功了一半。

1.2 技术选型背后的核心逻辑

技术选型是每个毕设项目的第一步,也是最容易翻车的一步。这个题目我推荐的技术组合是:Spring Boot 2.7.x + MyBatis-Plus + MySQL + Redis + Vue 3。每个选型背后都有明确的原因,不是为了炫技。

先说版本问题。Spring Boot 3.x 已经发布,但它要求 JDK 17 起步,很多学生的电脑上装的是 JDK 1.8,学校机房更是普遍老环境。如果盲目选新版本,到了部署阶段会非常痛苦。Spring Boot 2.7.x 是 2.x 系列的最后一个稳定版本,既能稳定运行在 JDK 1.8 上,又兼容绝大多数第三方组件,是目前做毕设最稳妥的选择。热点词里频繁出现的“springboot 版本太高”“idea 不能创建 springboot 项目不能使用 jdk1.8”,根源基本都是 3.x 和旧 JDK 的兼容问题,我在第 4 章会详细展开。

再看 MyBatis-Plus。它没有完全替代 MyBatis,而是在 MyBatis 基础上提供了通用 Mapper、条件构造器、分页插件等工具,写简单的单表 CRUD 几乎不用自己写 SQL。这个项目里站点管理、用户管理等都是典型的单表操作,MyBatis-Plus 能把代码量压缩一大半。更关键的是,它内置的代码生成器可以一键生成 entity、mapper、service、controller,非常适合毕设这种开发周期有限的场景,也能让你把精力集中在换乘算法这类核心业务上。

最后是 Redis。可能有人觉得一个毕设用 Redis 有点多余,但交通线路查询系统有一个很典型的特点:线路数据基本不变,但查询频率极高。同一个热门线路,一天可能被查成百上千次,这些查询如果每次都打到数据库,既慢又没必要。把线路查询结果缓存到 Redis,设置合理的过期时间,接口响应速度会有肉眼可见的提升。而且“redis 在 springboot 中的使用”本身就是面试高频考点,做了这个点,简历上又能多写一条。

1.3 数据库设计:一张关联表盘活全局

数据库设计是这类查询系统最关键的一步,设计不好,后面的算法和接口都跟着难受。我见过不少学生把“线路”和“站点”塞进一张表里,用逗号分隔字段存一堆站点名,查询的时候用正则去拆,这种做法在数据量小的时候能跑通,但没有任何扩展性,答辩时也经不起追问。

我推荐用四张核心表来建模:

表名关键字段作用
t_lineid, line_name, line_type, start_time, end_time, price存储线路基本信息,line_type 区分公交/地铁/长途
t_stationid, station_name, lat, lng存储站点信息,经纬度字段为后续扩展距离计算做准备
t_line_stationid, line_id, station_id, station_order, distance_next线路与站点的关联表,station_order 记录站点在线路上的先后顺序
t_userid, username, password, phone, create_time用户信息,用于注册登录和收藏功能

核心是 t_line_station 这张关联表。它把“线路”和“站点”的多对多关系解耦出来,每一条记录代表“某条线路上的某个站点,以及它在整条线路上的顺序位置”。为什么要单独存 station_order?因为线路查询最关键的就是顺序。比如 1 路公交从 A 站开到 E 站,中间经过 B、C、D,站点之间是有先后关系的,只有存下顺序,才能算出“从 A 站坐 3 站到 D 站”这种结果。

这里有一个我在实际设计中反复强调的细节:关联表里最好再加上 next_distance 字段,记录当前站点到下一站的距离。有了它,换乘方案可以算出总距离,给用户提供一个“距离优先”的排序条件。如果一开始没设计这个字段,后面想补就得改表结构,数据迁移很麻烦。

1.4 项目目录结构与代码分层

Spring Boot 项目不是把代码随便堆在一起就行,清晰的包结构对毕设的意义在于:答辩时能讲出逻辑,后续加功能时不会乱。我的习惯是建一个简洁的 controller/service/mapper/entity 四层结构,再额外加一个 common 包放统一返回结果和异常处理。

com.example.transit ├── controller // 接口层,接收请求,返回统一结果 ├── service // 业务层,线路查询、换乘计算等核心逻辑 ├── mapper // 数据访问层,继承 BaseMapper 接口 ├── entity // 实体类,与数据库表字段对应 ├── dto // 数据传输对象,用于接口入参和出参 ├── common // 统一返回结果、异常处理、常量类 └── config // 配置类,如 Redis 配置、MyBatis-Plus 配置

这样的分层方式和后端开发岗位的主流做法基本一致,以后出去实习也能很快适应。有一点要注意:不要把业务逻辑写在 Controller 里。之前看过一个学生的代码,换乘算法直接在 Controller 方法里写了 200 行,接口一多整个类就变得没法维护。业务逻辑放在 Service 层,Controller 只做参数接收和结果返回,这既是规范问题,也是代码可读性的问题。

2. 核心功能拆解与 Spring Boot 落地要点

2.1 换乘方案怎么算:从 BFS 到最少换乘策略

查询功能听起来简单,但“换乘方案计算”是整个项目真正的技术难点,也是答辩时老师最可能追问的地方。直白地说,从一个站点到另一个站点,如果两条线路能直达,问题很简单;但现实场景里更常见的是一趟线路到不了,需要中途换乘。怎么用代码找到“换乘次数最少”的方案,这就要用到图论里的思路。

我先把问题抽象成图模型:把每个站点看成图的顶点,把每条线路看成“可以一次性到达的边集合”。注意这里我们不能直接把线路当边,因为换乘约束的是“在同一站点从线路 A 换到线路 B”,所以要基于“线路”而不是“站点”来建图。

实现时我推荐用 BFS(广度优先搜索),因为 BFS 天然适合求“最少换乘次数”。伪代码逻辑是这样的:从起点站出发,先找到所有经过起点站的线路;再从这些线路上的每一个站点出发,找所有经过该站点的其他线路;逐层扩展,直到找到包含终点站的线路。每一层扩展就是一次换乘。

public List<TransferPlan> findTransferPlans(Integer startStationId, Integer endStationId) { // 1. 找到所有经过起点站的线路 List<Line> startLines = lineStationMapper.selectLinesByStationId(startStationId); // 2. 如果某条线路直接经过终点站,直接返回直达方案 // 3. 否则用队列做 BFS,记录换乘路径 Queue<List<Integer>> queue = new LinkedList<>(); // 存储线路 id 序列 Set<Integer> visited = new HashSet<>(); for (Line line : startLines) { List<Integer> path = new ArrayList<>(); path.add(line.getId()); queue.offer(path); } while (!queue.isEmpty()) { List<Integer> currentPath = queue.poll(); Integer currentLineId = currentPath.get(currentPath.size() - 1); // 4. 如果当前线路经过终点站,返回方案 // 5. 否则遍历当前线路所有站点,再找经过这些站点的其他线路,加入队列 } }

这里一个关键设计是 visited 集合,记录哪些线路已经被访问过,避免 BFS 死循环。试想一下,A 线路经过站点 X,X 又连着 B 线路,而 B 线路又经过站点 Y,Y 又连回 A 线路,如果没有 visited 标记,程序就会在两条线路之间无限循环。

2.2 站点与线路关键词搜索的优化思路

除了换乘查询,用户使用频率最高的就是搜索框。用户输入“火车站”或“人民广场”,系统要能快速返回匹配的站点或线路列表。最常见也最容易被学生写错的地方,就是用LIKE '%关键词%'直接查数据库,数据量小的时候没问题,可一旦站点表超过几万条,这种写法性能会明显下降。

更合理的做法是分两步走。第一步,先把高频搜索词的结果放进 Redis 缓存,比如用户搜过“火车站”,系统把站点列表缓存起来,第二次再搜直接命中缓存,不再查数据库。第二步,主查询用 MySQL 的索引优化,站点名称字段建立普通索引,配合LIKE '关键词%'前缀匹配,让数据库走索引而不是全表扫描。

缓存这块直接使用 Spring 的@Cacheable注解就能实现,不过我更推荐手动用 RedisTemplate 来实现,原因是在毕设答辩时,老师如果问“这个查询为什么快”,你能说出“先查缓存、缓存没有再查数据库、回填缓存”的完整链路,比笼统说一句“用了缓存注解”更有说服力。

public List<Station> searchStations(String keyword) { // 1. 先从 Redis 查 String cacheKey = "station:search:" + keyword; Object cached = redisTemplate.opsForValue().get(cacheKey); if (cached != null) { return JSON.parseArray(cached.toString(), Station.class); } // 2. 缓存没有,查数据库 List<Station> stations = stationMapper.selectList( new LambdaQueryWrapper<Station>() .likeRight(Station::getStationName, keyword)); // 3. 回填缓存,设置 30 分钟过期 redisTemplate.opsForValue().set(cacheKey, JSON.toJSONString(stations), 30, TimeUnit.MINUTES); return stations; }

有一点要提醒:缓存数据一定要设置过期时间,否则线路调整后,用户看到的还是旧数据。公共交通信息偶尔是会发生变动的,哪怕是毕设,也要把“缓存失效”这件事考虑进去。

2.3 Spring Boot 自动装配机制在项目里的体现

做这个项目时,很多学生都会被 Spring Boot 那一堆注解搞晕:@SpringBootApplication@RestController@Service@Autowired@ConfigurationProperties,这些注解到底是怎么生效的?我在带学生的过程中发现,与其死记硬背注解用法,不如把 Spring Boot 的“自动装配”原理弄明白,理解了它,所有注解都变得顺理成章。

简单说,Spring Boot 的启动类上的@SpringBootApplication是一个组合注解,包含@SpringBootConfiguration@EnableAutoConfiguration@ComponentScan。其中@EnableAutoConfiguration是核心,它通过读取依赖 jar 包里的META-INF/spring.factories文件,找到大量配置类,再通过@ConditionalOnClass@ConditionalOnMissingBean这些条件注解判断“当前项目有没有这个类、有没有这个Bean”,有就自动配置。这就是为什么你只要引入spring-boot-starter-data-redis依赖,Spring Boot 就会自动帮你创建 RedisTemplate 的实例,前提是你配好了连接信息。

理解这个原理后,面试题“springboot 自动装配原理”就能答得很清楚了。更实际的好处是,项目里如果要自定义一个全局配置,你就知道该用@ConfigurationProperties配合@EnableConfigurationProperties把配置绑定到实体类上,而不是在代码里各处写@Value取配置。配置一多,@Value的写法会显得特别乱。

2.4 统一返回结果与全局异常处理

接口返回格式在前后端分离项目里非常重要。如果你让每个接口返回的数据结构都不一样,前端对接的时候会哭死。我习惯定义一个统一的结果类 Result,包含 code、message、data 三个字段。成功时 code 为 200,失败时 code 为 500,这样前端只需要判断一次 code 就能走完整个流程。

全局异常处理用@RestControllerAdvice配合@ExceptionHandler实现。这个类可以把所有 Controller 层抛出的异常统一拦截,转成 Result 对象返回,避免出现一长串堆栈信息直接暴露给前端的情况。除了业务异常,还可以在全局异常里处理参数校验异常、数据库访问异常等,前端只需要针对不同的 code 给出不同提示。

3. 实操过程与核心环节实现

3.1 从零搭建项目:版本、初始化和 Maven 配置

很多学生的第一个坑就出现在创建项目这一步,尤其是 IDA 创建 Spring Boot 项目时选不到合适的版本,或者下载依赖慢得像蜗牛。这里我直接给一套稳妥的搭法。

第一步,确认本机环境。JDK 1.8 + Maven 3.6.3 是经典组合,不需要追求高版本。IDEA 不建议用太老的版本,2022.x 以上对 Spring Boot 的支持都很成熟。很多人问 Gradle 和 Maven 选哪个,毕设我强烈建议 Maven,因为搜索资料时 Maven 的解决方案更多,遇到坑更好查。

第二步,创建项目时,注意 Spring Initializr 服务地址。如果你用的是 IDEA 自带的 start.spring.io,在国内网络环境下经常超时。这里可以换成阿里云的初始化服务地址,创建速度快得多,而且它默认提供的依赖版本更符合国内开发环境的习惯。这个细节看起来不起眼,但能帮你省下大量等待时间。

第三步,配置 Maven 的仓库镜像。打开 Maven 的 settings.xml 文件,把中央仓库地址改为阿里云镜像。改完之后,下载 Spring Boot 相关依赖的速度会有质的提升。一个古典的问题就是:pom.xml 里依赖还没下完就急着写代码,结果代码里所有 import 全部标红,这是网络问题,不是代码问题。

创建完成后,检查 pom.xml 里的 Spring Boot 版本。如果是 2.7.x,可以放心用 JDK 1.8;如果你不小心选到了 3.x 版本,而本机是 JDK 1.8,那就要把版本改回 2.7.x,否则启动会直接报错。版本对应关系我放在第 4 章,方便对照。

3.2 配置文件:数据源、Redis 与 MyBatis-Plus 的整合

Spring Boot 的配置集中在 application.yml 里,把这个文件配好,项目就等于成功了一半。下面是一份可以直接参考的核心配置:

server: port: 8080 servlet: context-path: /api spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/transit_system?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai username: root password: 123456 redis: host: localhost port: 6379 database: 0 timeout: 5000ms mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl global-config: db-config: logic-delete-field: deleted logic-delete-value: 1 logic-not-delete-value: 0

有几个容易被忽略的点。MySQL 连接地址必须加上 serverTimezone 参数,不然会有时区报错。MyBatis-Plus 的log-impl配置建议开发阶段保留,它会在控制台打印每一条 SQL,排错时非常有用;但上线前记得关掉,否则日志文件会非常大。

如果要做多数据源,比如同时连 MySQL 和 SQLServer,可以引入 dynamic-datasource 依赖,然后在配置里写多个 datasource,Service 层用@DS("slave")注解指定走哪个数据源。这个点可以作为项目的加分项,但前提是你确实有这样一个业务需求,不然就是画蛇添足。

3.3 核心代码实现:换乘查询接口的完整链路

换乘查询是整个系统最核心的接口,我们从头到尾串一遍。后端接口定义为POST /api/line/transfer,入参是起始站点名称和目标站点名称,出参是换乘方案列表。

第一步,Controller 接参并调用 Service:

@RestController @RequestMapping("/line") public class LineController { @Autowired private TransferService transferService; @PostMapping("/transfer") public Result<List<TransferPlanVO>> transfer(@RequestBody TransferQueryDTO dto) { List<TransferPlanVO> plans = transferService.queryTransferPlan(dto.getStartStation(), dto.getEndStation()); return Result.success(plans); } }

第二步,Service 里做核心计算。先用 Redis 缓存判断是否已有结果,有就直接返回,没有再执行 BFS 换乘算法。

第三步,算法拿到换乘线路序列后,还要补上每个换乘站点的名称、乘坐方向、途经站数。这一步需要写一个辅助方法,通过 t_line_station 表找出“换乘站点在两条线路上的具体位置”,比如从 1 路换到 5 路,要能算出你在 1 路上坐了几站、在 5 路上准备坐几站,换乘点在哪。

换乘站点的上下车站关系,是这个项目最绕的地方。我的建议是写一个 LineStationService,专门处理“线路经过的站点有序列表”这类查询,核心业务直接复用,不要每个方法都写一遍 SQL。代码写完之后,最好手工构造几组测试数据验证一下算法的正确性,不要直接把网上扒下来的数据塞进去,否则数据格式不一致很容易出现查不到方案的情况。

3.4 前端 Vue 对接与跨域处理

前端用 Vue 3 + Element Plus 是当下最常见的组合,和后端通过 RESTful API 对接。前端项目里需要配置一个代理,把/api开头的请求转发到后端的localhost:8080

vite.config.js 里的核心配置如下:

export default defineConfig({ server: { port: 3000, proxy: { '/api': { target: 'http://localhost:8080', changeOrigin: true } } } })

很多学生第一次联调时看到浏览器控制台报跨域错误,第一反应是去后端加@CrossOrigin注解,这其实只解决了一半问题。有了上面的代理配置,前端访问http://localhost:3000/api/line/transfer,Vite 开发服务器会自动帮你转发到后端,浏览器和前端服务器之间不存在跨域,自然也就没有跨域问题了。后端加的@CrossOrigin属于备份方案,两边都配上也没有问题。

前端页面重点做好三块:线路查询页(输入起点和终点,展示换乘方案列表)、站点搜索页(输入关键词展示站点和经过线路)、后台管理页(管理员维护线路与站点数据)。页面数量不用太多,但每个页面都要和真实业务挂钩,尤其要注意展示换乘方案时,把“乘坐 X 路,从 A 站上车坐 4 站到达 B 站,换乘 Y 路”这类描述清晰呈现给用户,这是用户体验的关键。

如果 2025 年的你想给项目加一些亮点,可以考虑把 Spring Boot MCP 引入进来,把线路查询能力封装成 AI 助手可以调用的工具,用户通过聊天就能问“从人民广场到火车站怎么走”。但那属于进阶玩法,先把基础查询功能做扎实再说。

4. 常见问题与排查技巧实录

4.1 Spring Boot 版本太高引发的连环坑

我见过太多学生在这个问题上浪费时间。症状通常是这样的:电脑装了 JDK 21,用 IDEA 新建 Spring Boot 项目时,默认拉到 3.x 版本,代码写完后启动直接报错。报错信息五花八门,最多的是java.lang.UnsupportedClassVersionError,意思是编译版本的 class 文件在当前 JVM 上跑不了。

问题的根源在于版本匹配。Spring Boot 3.x 要求 JDK 17 及以上版本,内部包名也从javax.*改成了jakarta.*。如果强行用 JDK 1.8 跑 Spring Boot 3.x 项目,包名就找不到,代码根本编译不过。反过来,JDK 21 跑 Spring Boot 2.7.x 也偶尔会有兼容问题。

我给你的建议,也是我反复验证过的版本组合表:

Spring Boot 版本JDK 版本包名适用场景
2.7.xJDK 8 / 11javax.*毕设推荐,资料多,兼容好
3.0.x - 3.2.xJDK 17 / 21jakarta.*新项目可以试,但资料相对少
3.3.x 及以上JDK 17 / 21jakarta.*生产级新特性,毕设不推荐

如果电脑上已经装了高版本 JDK,又不想卸载重装,可以在 IDEA 的 Project Structure 里单独给项目指定 JDK 1.8,前提是你机器上必须真实安装了 JDK 1.8,只在 IDEA 里选没有用。装了多个 JDK 时,还要检查JAVA_HOME环境变量指向谁,IDEA 里 Maven 用的 JRE 配置也要跟着改,三个地方保持一致,才不会启动时报找不到主类。

4.2 数据库连接与缓存相关的坑

开发过程中,数据库连接问题是最常见的。报错Access denied for user 'root'@'localhost',说明数据库账号或密码不对。报错Unknown database 'transit_system',说明你还没有创建这个数据库,用 Navicat 或命令行执行CREATE DATABASE transit_system DEFAULT CHARACTER SET utf8mb4;就能解决。注意字符集一定要指定 utf8mb4,不然中文站点名存储和查询都会出问题。

还有一个很经典的坑:Public Key Retrieval is not allowed。这个错误出现在 MySQL 8.0 以上版本,连接 URL 里加上allowPublicKeyRetrieval=true即可。MySQL 驱动版本也要注意,Spring Boot 2.7.x 默认引入的是 MySQL Connector/J 8.0.x,如果你手动在 pom.xml 里换成了 5.1.x,驱动类和 URL 写法都会不一样,这也是查都查不出来的隐藏问题。

Redis 连接不上是第二大类问题。检查三个地方:Redis 服务有没有启动(命令行redis-cli ping返回 PONG 说明正常)、配置文件的端口是否一致、Redis 是否设置了密码。这里有一个经常被忽略的细节:Spring Boot 2.x 里连接 Redis 默认用的 Jedis,需要单独引入依赖;而 Spring Boot 2.7.x 如果只想快速使用,可以直接用spring-boot-starter-data-redis,它默认用的是 Lettuce,不需要额外配置就能跑起来。我建议毕设用 Lettuce 就行,少一个依赖少一个坑。

4.3 启动异常与依赖冲突排查

Spring Boot 项目启动失败是一个高频问题。排查时建议遵循“看日志、定位、再看日志”的顺序,不要瞎改代码。启动时最常看到的错误是端口占用:Port 8080 was already in use。这种情况在 Windows 上很常见,之前的项目没关干净就重新启动。命令行执行netstat -ano | findstr 8080找到占用端口的进程 PID,然后在任务管理器里结束它就行。

依赖冲突的问题也很典型,症状是启动时出现NoSuchMethodErrorClassNotFoundException,这类问题在排查时先看 pom.xml 里有没有重复引入相同功能的依赖,再看依赖版本是否有冲突。常用的办法是执行mvn dependency:tree查看依赖树,找出冲突的 jar 包,用exclusion排除掉不需要传递引入的依赖。这个时候你会发现,依赖尽量少而精才是王道,不要什么都往 pom.xml 里加。

> 提示:如果你遇到 `Failed to configure a DataSource: 'url' attribute is not specified` 这个错误,先不要慌。这个报错的本质是 Spring Boot 启动时自动装配了数据源,但找不到连接配置。检查一下 application.yml 是否被正确加载,或者你在测试类里启动了 Spring 上下文但没配置数据源。最简单的解决方法是把测试类加上 `@SpringBootTest`,并且保证测试环境有完整的配置。 ### 4.4 答辩高频追问整理:理解比背答案更重要 做毕设最终要过答辩这一关,我把带学生时被问得最多的几个 Spring Boot 相关问题整理出来,每个都附带通俗的解释思路。 第一个问题几乎是必问的:Spring Boot 自动装配的原理是什么?回答思路是:启动类上的 `@SpringBootApplication` 包含 `@EnableAutoConfiguration`,它会扫描所有 jar 包里的 `META-INF/spring.factories` 文件,把里面声明的配置类加载进来,再通过 `@ConditionalOnClass` 等条件注解判断这些配置是否生效。最后利用 `@ConfigurationProperties` 把配置文件里的值绑定到属性类上,完成自动配置。 第二个问题:Spring Boot 的 starter 是什么?为什么引入一个依赖就能用?回答思路:starter 是一组相关依赖的组合包,比如 `spring-boot-starter-web` 内部把 Spring MVC、Tomcat、Jackson 这些 Web 开发需要的库都打包好了,你不必一个个去引依赖。加上 `@EnableAutoConfiguration` 的自动装配,就实现了“引入即用”。 第三个问题:项目里的事务怎么处理?哪些场景事务会失效?这个项目里,线路和站点新增、修改属于写操作,建议在 Service 方法上加上 `@Transactional`。事务失效的常见场景包括:方法内部自调用(同类里调用另一个 `@Transactional` 方法)、异常被 catch 住导致事务感知不到、访问权限不是 public 导致 Spring AOP 无法代理。回答时能举一个自己项目中实际的例子,比单纯背理论高出一个档次。 第四个问题:为什么用 Redis 而不是直接查数据库?回答思路:交通线路数据变化频率低、查询频率高,属于典型的“读多写少”场景。把高频查询结果缓存到 Redis,可以减少数据库压力、降低接口响应时间。同时还要强调,我设置了合理的过期时间,并且在线路数据更新时会主动删除缓存,保证数据的最终一致性。这套思路说完,基本就能让老师看到你是有真实项目经验的。 ### 4.5 避坑速查表 下面这张表是我整理的高频问题速查,建议你保存一份,做项目过程中遇到问题先来查一遍再开百度: | 问题现象 | 常见原因 | 快速解决方案 | |---|---|---| | 创建项目时下载依赖失败 | 中央仓库访问慢 | 配置阿里云 Maven 镜像 | | 启动报 UnsupportedClassVersionError | JDK 与 Spring Boot 版本不匹配 | 按版本对应表调整 | | 端口被占用 | 上一次程序未停止 | netstat 查 PID,任务管理器结束进程 | | 数据库连不上 | 密码错误、数据库未创建 | 检查连接配置和数据库名称 | | 中文乱码 | 数据库字符集不是 utf8mb4 | 统一设置 utf8mb4 | | Redis 连接超时 | Redis 服务未启动或密码错误 | 检查服务状态和密码配置 | | 接口返回 500 | 全局异常没有兜底 | 写一个全局异常处理类 | | 前端跨域报错 | 前后端地址不一致 | 用 Vite proxy 配置代理 | 我发现一个规律:学生做项目花在“配置问题”上的时间,往往比写业务代码还多。这其实是正常现象,我刚接触 Spring Boot 那几年,也经常在 Maven 依赖和版本问题上折腾到大半夜。但正是因为踩过这些坑,后来看项目报错才会条件反射般地想到定位方向,这算是这个阶段最宝贵的收获。 做这个题目,我最后总会提醒学生一件事:不要急着追新版本、抄大项目,把 Spring Boot 本身的基本功打牢,把换乘算法讲明白,把一个查询接口的完整链路吃透,这个毕业设计就已经达到了“优秀”的底线。等你把自动装配、缓存、事务这些点都弄懂,你会发现很多看上去高大上的项目,核心原理也就是这么回事。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询