
1. 项目概述1.1 核心需求解析高校专业实习管理是很多软件工程、计算机类专业学生毕业设计里绕不开的一个题目。表面上看它是一个教务管理系统的变种但实际上它踩中了真实业务场景里最痛的几个点学生找实习、学校管过程、老师做评价、企业要反馈。这套系统的核心价值不在于录入数据而在于把一段长达数月、分散在多方的实习过程用一套流程串起来让每个角色都能随时知道现在进行到哪一步了。从技术角度讲这类系统的后端几乎默认选择SpringBoot。原因很简单SpringBoot对中小企业级Web应用的覆盖度太高了内置Tomcat、自动配置、生态成熟搭配一套Vue3前端就能组成完整的毕业设计技术栈。我见过大量同类项目的前后端交互设计几乎所有学生的选型逻辑都是一样的后端SpringBoot处理业务逻辑和数据持久化前端Vue3做页面渲染和状态管理中间用Restful API通信。如果你正准备做这个题目这篇文章会完整拆解系统的设计思路、核心功能模块的落地方式、实操过程中的关键代码细节以及毕业后真正被面试官问到的那些SpringBoot原理点。从搭项目骨架到部署上线一条线走完。1.2 业务流程全景实习管理系统的业务复杂度很多人一开始是低估的。你以为它只是一个CRUD系统写几个Controller就能交差但实际上它包含了一条完整的状态流转链路学生登录系统查看企业发布的实习岗位在线提交申请企业导师或学院管理员审核通过后学生进入实习阶段。实习期间学生需要定期提交周报记录工作内容企业导师给出阶段性评价学校指导教师不定期查看实习进度。实习结束后学生提交实习总结报告企业导师给出综合评价学院根据过程数据和最终评价自动汇总成绩。整个流程下来至少涉及五个角色的状态交互。这种业务模型的本质是一个工作流系统。开发时最忌讳的就是把所有状态判断堆在Service层里写if-else一旦实习流程出现撤回申请重新审核延期实习这类分支操作代码会迅速腐化。所以设计时要在数据库层面就把状态字段、操作记录、时间线分开存储每一条状态变更都留痕。这也是后期答辩时最出彩的一个设计亮点。2. 系统功能模块设计2.1 用户角色与权限体系整个系统的角色划分直接决定权限模块的复杂度。我建议按六种角色来设计学生、企业导师、学院教师、教学秘书、系统管理员、超级管理员。其中学院教师和教学秘书的权限要仔细区分教师只能看到自己指导的学生教学秘书可以看到整个专业的实习数据。SpringBoot侧实现权限控制时我推荐使用Sa-Token而不是Shiro或Spring Security。理由有三点第一Sa-Token的API设计非常直观StpUtil.login()、StpUtil.checkLogin()一行代码完成会话管理对毕业设计这种时间紧张的项目非常友好第二它内置了权限校验注解SaCheckPermission可以直接标注在Controller方法上省去写拦截器的麻烦第三它支持Redis集成分布式场景下也能平滑过渡。权限数据模型上我采用了RBAC设计五张表搞定用户表、角色表、权限表、用户角色关联表、角色权限关联表。这里有一个容易被忽略的细节——不要只在前端做菜单级别的权限控制后端Controller层的每个接口都必须做细粒度权限校验。我之前帮人排查过一个系统前端隐藏了删除岗位按钮但接口没做权限校验直接调用API就能删数据这是非常严重的安全漏洞。2.2 核心功能模块拆解整个系统按业务边界拆成六大模块实习岗位管理、实习申请审批、实习过程监管、周报与指导记录、考核评价管理、数据统计导出。下面详细拆解每个模块的设计要点。实习岗位管理模块承担的是企业信息维护和岗位发布功能。企业信息建议单独建表一个企业可以发布多个岗位。岗位表的核心字段除了岗位名称、工作地点、薪资待遇、岗位要求之外还要加上招聘状态和实习周期。这个模块的难点在于岗位过期自动下架的逻辑我采用的方式是定时任务每天扫描一次将截止日期小于当前日期的岗位批量置为已截止状态。实习申请审批模块是整个系统状态流转最复杂的地方。学生提交申请后申请记录表会生成一条数据状态为待审核。企业导师审核通过后状态变为已通过同时系统自动生成一条实习记录关联到学生和岗位。这个过程必须用数据库事务保证原子性——审核通过的同时必须创建实习记录任何一个操作失败都要回滚。我在Service方法上标记Transactional并且合理设置了事务的传播行为。实习过程监管模块解决的是学生到底有没有认真实习这个问题。我的方案是让学生提交周报和实习打卡记录。周报表包含实习记录ID、周次、工作内容、心得体会、遇到的问题及解决方案打卡记录表则记录打卡时间、定位信息、打卡备注。打卡功能如果要做定位前端使用高德地图API获取经纬度后端计算与实习企业地址的距离超出500米则标记为异常打卡。考核评价模块的评分逻辑需要仔细设计。我采用的是百分制加权评分模型企业导师评价占60%学院指导教师评价占40%。企业导师从工作态度20分、专业能力30分、团队协作20分、出勤情况30分四个维度打分学院教师则根据周报质量50分、实习总结报告50分给出成绩。最终系统自动计算综合得分并映射为五级制成绩优秀、良好、中等、及格、不及格。数据统计导出模块是答辩时的亮点模块。实习状态分布、专业方向就业率、企业满意度、实习成绩分布这四类统计图几乎必做前端使用ECharts渲染后端通过SQL聚合查询返回统计数据。导出功能使用EasyExcel支持实习名单、考核成绩、实习报告摘要的批量导出。很多人在做导出时踩过坑——千万不要在前端循环调接口逐条导出一定要后端生成文件返回URL前端用window.open或a标签下载。3. 技术选型与项目搭建3.1 SpringBoot版本选择的经验之谈SpringBoot版本选择是这类项目第一个容易踩坑的地方。springboot版本太高这个热词背后全是血泪教训。我的建议是不要追新。以当前生态来看SpringBoot 2.7.x 是毕业设计项目最稳妥的选择。为什么不用3.x因为SpringBoot 3.x 基于Jakarta EE 9规范包名从javax.*变成了jakarta.*很多网上能搜到的老教程直接失效。而且3.x版本最低要求JDK 17如果你本机是JDK 8强行用3.x会直接启动失败。校园里机房、实验室的机器JDK版本普遍还是8或11选SpringBoot 2.7.x加JDK 8是完全无痛的组合。另一个要注意的点是SpringBoot与SpringCloud版本对应关系。如果项目只是单体应用不要引入SpringCloud相关组件徒增复杂度。我见过有人毕设里引入了Nacos、OpenFeign、Gateway结果项目启动慢、配置复杂、问题排查困难最后答辩时讲都不好讲清楚。单体应用就老老实实用SpringBoot自带的能力把业务做扎实比堆技术栈有意义。3.2 项目结构搭建我推荐的分层结构如下com.example.internship ├── config // 配置类跨域、拦截器、文件上传、定时任务 ├── controller // 控制层接收请求、参数校验、返回统一响应 ├── service // 业务层核心业务逻辑、事务控制 │ └── impl // Service实现类 ├── mapper // 数据访问层MyBatis-Plus的Mapper接口 ├── entity // 实体类与数据库表结构对应 ├── dto // 数据传输对象接收前端参数 ├── vo // 视图对象返回前端数据 ├── common // 通用类统一返回结果、异常处理、常量、枚举 ├── utils // 工具类JWT工具、Excel工具、日期工具 └── aspect // 切面类操作日志记录Controller只做参数接收和响应封装不写业务逻辑Service层做实际业务处理Mapper层只负责数据库交互。这个分层习惯从毕设养成到了工作中会受益无穷。你去看SpringBoot相关的面试题十道里有八道都在问说说你对分层架构的理解没有实际项目经验支撑很难答出深度。对于数据访问层推荐MyBatis-Plus而不是原生MyBatis。原因不用多说——单表CRUD的代码量差了一个数量级BaseMapper内置的selectById、insert、updateById直接省掉大量重复mapper XML。复杂查询用LambdaQueryWrapper构造条件比如查询某个专业所有已通过审核的实习记录一行代码搞定。// 查询条件构造示例 LambdaQueryWrapperInternshipRecord wrapper new LambdaQueryWrapper(); wrapper.eq(InternshipRecord::getStudentId, studentId) .eq(InternshipRecord::getStatus, 已通过) .orderByDesc(InternshipRecord::getCreateTime); ListInternshipRecord records internshipRecordMapper.selectList(wrapper);3.3 数据库表结构设计数据库设计是整个系统的地基。我设计时遵循以下核心表用户表user、角色表role、实习岗位表internship_position、企业信息表company、实习申请表internship_application、实习记录表internship_record、周报表weekly_report、打卡记录表attendance_record、考核评价表evaluation、通知公告表notice。再加一张操作日志表记录关键操作行为。有两张表的细节需要特别关注。第一张是实习记录表它是连接申请、过程、考核的枢纽建议字段包括主键ID、学生ID、岗位ID、企业导师ID、学院教师ID、开始时间、结束时间、当前状态、综合成绩。第二张是考核评价表建议设计为一条实习记录对应一条总评记录同时关联两张子表存储企业导师评价明细和学院教师评价明细这样查询总评时只需一次关联查询。字段设计的几个实战建议所有金额字段用DECIMAL(10,2)不用FLOAT/DOUBLE避免精度丢失所有时间字段统一用DATETIME不用TIMESTAMP规避2038年问题业务状态字段用TINYINT存数字枚举配合统一枚举类做映射学生学号和企业统一社会信用代码一定要加唯一索引避免重复录入。这些细节在面试时讲出来比你背十道面试题管用得多。4. 核心功能落地实操4.1 SpringBoot整合MyBatis-Plus与数据访问数据访问层是整套系统的地基工程SpringBoot整合MyBatis-Plus的步骤看起来简单但配置细节直接决定后期开发效率。第一步在pom.xml引入依赖dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-boot-starter/artifactId version3.5.3.1/version /dependency第二步在application.yml配置数据源和MyBatis-Plus相关参数。我通常会在配置里加上map-underscore-to-camel-case: true让数据库下划线字段自动映射为Java驼峰属性逻辑删除配置logic-delete-field: deleted配合实体类的TableLogic注解实现软删除分页插件单独放在config类中注册。Configuration public class MybatisPlusConfig { Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; } }分页插件一定要配置不然Page对象不会自动拼接LIMIT语句。我当时做学生列表分页时忘记注册这个Bean查出来的数据永远是全量的排查了很久才发现是分页插件没有生效。这个配置做到配置类里以后所有分页查询都会自动生效前端传current和size两个参数即可。实体类通过TableName注解关联数据库表TableId(type IdType.AUTO)设置主键自增策略TableField处理字段映射。还有一个小技巧项目开启MyBatis-Plus的SQL日志打印功能在application.yml里配置mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl开发阶段开着日志控制台能直接看到拼接的SQL语句排查问题效率提升一大截。上线前记得关掉或调整日志级别避免性能损耗和SQL语句泄漏。4.2 实习申请流程的状态机设计实习申请模块是整个系统里业务逻辑最密集的地方。我强烈建议用状态机思想设计而不是随手写if-else。定义一个枚举类存所有申请状态public enum ApplyStatus { PENDING(0, 待审核), APPROVED(1, 已通过), REJECTED(2, 已驳回), WITHDRAWN(3, 已撤回), CANCELED(4, 已取消); private final Integer code; private final String desc; ApplyStatus(Integer code, String desc) { this.code code; this.desc desc; } // getter方法 }当学生提交申请时状态为待审核企业导师审核通过状态变为已通过同时创建实习记录审核驳回状态变为已驳回需要填写驳回原因学生在待审核状态下可以撤回申请状态变为已撤回若实习记录已生成但学生放弃实习状态变为已取消同时实习记录做作废处理。状态流转的合法性校验一定要写。比如已驳回的申请不能再走审核通过的逻辑已撤回的记录不能重复提交。我的实现方式是在Service层写一个状态机校验方法接收当前状态和期望操作返回是否允许流转不允许则直接抛出业务异常由全局异常处理器统一捕获返回前端。实习时长计算也是一个实用细节。系统根据岗位发布的实习周期和企业导师确认的起止时间自动计算学生实习天数。这块建议在生成实习记录时就算好存到数据库里而不是每次查询时实时计算因为后续周报和考核模块都会用到这个字段实时计算会带来重复逻辑和性能损耗。4.3 文件上传与在线预览实习管理系统中学生需要上传实习承诺书、实习报告等附件企业可能需要上传营业执照扫描件。文件上传是必不可少的功能。SpringBoot配置上传大小限制是第一步。默认配置1MB上传限制绝对要把spring.servlet.multipart.max-file-size和max-request-size调整到合理值。我的配置是单个文件10MB整个请求50MB。上传文件保存路径一定不要放在项目内部的resources下因为项目重新打包部署后文件会丢失。推荐配置外部路径比如Linux环境下的/www/upload/。上传接口的设计我建议返回文件ID和访问路径两个关键信息。文件ID用于业务表和附件表关联访问路径用于前端回显和下载。文件存储到本地服务器文件名不要用用户上传的原始文件名称而应该用UUID重命名避免出现中文乱码和特殊字符问题也避免文件名冲突。在线预览实习报告PDF文件有一种在毕业设计里很好用的方案是使用pdf.js在前端实现预览。后端提供一个文件流接口前端通过PDF.js加载并渲染。如果是图片文件直接返回文件流给img标签的src即可预览。Office文件预览最麻烦简单的做法是引导用户下载后用本地办公软件打开复杂的做法是接入OnlyOffice或KKFile在线预览服务但这类服务部署成本高毕业设计不推荐。PostMapping(/upload) public ResultString upload(RequestParam(file) MultipartFile file) { if (file.isEmpty()) { throw new BusinessException(上传文件不能为空); } // 校验文件类型只允许图片、PDF、Office文档 String originalFilename file.getOriginalFilename(); String suffix originalFilename.substring(originalFilename.lastIndexOf(.)); ListString allowSuffixList Arrays.asList(.jpg, .png, .pdf, .docx, .xlsx); if (!allowSuffixList.contains(suffix.toLowerCase())) { throw new BusinessException(不支持的文件类型); } // 使用UUID生成新文件名 String newFileName UUID.randomUUID().toString().replace(-, ) suffix; // 按月份分目录存储避免单个目录文件过多 String monthPath new SimpleDateFormat(yyyyMM).format(new Date()); String dirPath uploadPath / monthPath; File dir new File(dirPath); if (!dir.exists()) { dir.mkdirs(); } file.transferTo(new File(dir, newFileName)); return Result.success(/preview/ monthPath / newFileName); }4.4 定时任务与消息通知系统里的岗位自动下架周报提交提醒实习即将到期提醒都依赖定时任务。SpringBoot实现定时任务很简单启动类加EnableScheduling注解然后在方法上标注Scheduled(cron 0 0 2 * * ?)就能每天凌晨两点执行一次扫描逻辑。我用一个定时任务类处理三类提醒一是检查岗位截止日期将已过期岗位置为下架状态二是给超过三天未提交周报的学生发送站内信和邮件提醒三是实习结束前一周给学院教师发送审核提醒。执行频率别太频繁一般每天一次即可如果学校对实时性要求高可以把过期岗位检查改为每小时一次。消息通知用两个渠道就够了系统站内信 邮件通知。站内信表设计很简单ID、接收用户ID、标题、内容、是否已读、创建时间。邮件通知使用SpringBoot的spring-boot-starter-mail依赖QQ邮箱或126邮箱的SMTP服务足够。发邮件前一定要确认SMTP授权码配置正确很多人在这一步卡住报javax.mail.AuthenticationFailedException基本就是授权码填成了邮箱密码。我踩过的另一个坑是定时任务方法内部出现了异常整个任务线程被中断后续任务不再执行。解决方法是每个任务内部用try-catch包裹独立逻辑甚至给每个任务单独设置异常处理确保一个任务失败不影响其他任务。5. 常见问题与排查实录5.1 SpringBoot版本太高导致的依赖冲突SpringBoot版本太高这个热词在实战中是真的会发生。SpringBoot 3.x发布后很多同学直接用了最新版然后发现之前用的mybatis-plus-boot-starter版本过低启动报ClassNotFoundException: javax.servlet.Filter。原因是SpringBoot 3.x迁移到了Jakarta命名空间旧包里的javax.servlet相关类全部失效。查了下MyBatis-Plus官方需要3.5.4以上版本才适配SpringBoot 3.x。如果你在pom里看到类似下面的报错先别慌版本对齐是解决路径*************************** APPLICATION FAILED TO START *************************** Description: An attempt was made to call a method that does not exist. The attempt was made from the following location: ... Caused by: java.lang.NoClassDefFoundError: javax/servlet/Filter我的建议方案是直接降级到SpringBoot 2.7.x同时保持JDK 8。我不止一次在项目维护中遇到过这种版本牵连的连环问题对于以业务落地为核心的毕业设计项目稳定压倒一切。如果你已经用SpringBoot 3写了不少代码另一个方案是把MyBatis-Plus升级到3.5.5并且把代码里所有javax包替换为jakarta但这项工作很容易遗漏不如降级来得干脆。5.2 多角色登录态管理与跨域联调前端Vue3和后端SpringBoot通常不在同一台服务器上开发调试跨域问题一定会在联调阶段爆发。SpringBoot侧最简单的处理方式是新增一个配置类允许所有跨域请求Configuration public class CorsConfig implements WebMvcConfigurer { Override public void addCorsMappings(Registry registry) { registry.addMapping(/**) .allowedOriginPatterns(*) .allowedMethods(GET, POST, PUT, DELETE, OPTIONS) .allowedHeaders(*) .allowCredentials(true) .maxAge(3600); } }注意allowCredentials(true)和allowedOriginPatterns(*)需要配合使用如果只用allowedOrigins(*)会冲突报错。OPTIONS预检请求也要放行否则前端调用跨域接口时会看到请求被CORS策略阻止的错误。多角色登录态管理我采用的是Token方案。用户登录成功后后端生成一个JWT Token返回给前端存储到localStorage或pinia。前端在请求拦截器中把Token塞进Authorization请求头后端写一个拦截器统一解析Token并校验用户身份。一个值得留意的细节不同角色的登录入口可以共用同一个登录接口后端根据用户输入的账号去用户表查询拿到用户信息和关联的角色ID生成Token时把角色和权限点全部写进Token的claims里。这样后续做权限判断时直接从Token拿角色比对不需要频繁查数据库。我在面试中问候选人如何实现用户角色权限的动态刷新大部分人回答都是改数据库重新登录但实际项目中更优雅的做法是在Redis里存储权限版本号每次请求时比对版本版本变化就重新加载权限。5.3 部署中的真实问题清单部署是很多毕业设计项目最轻视、最后出洋相的一环。很多人的项目在自己电脑上跑得好好的一部署到服务器就各种启动失败。我把部署中遇到的典型问题整理成一份速查表现象可能原因解决方案项目打包后启动报找不到主类Maven打包插件未配置pom.xml中增加spring-boot-maven-plugin并设置mainClass端口被占用或外网无法访问服务器安全组未放行端口检查宝塔面板/云安全组规则放行8080或自定义端口数据库连接报Access denied生产环境数据库密码与本地不同使用配置文件外部化用--spring.datasource.passwordxxx启动参数覆盖上传的图片刷新后丢失文件路径使用相对路径改为绝对路径并配置静态资源映射访问外部目录前端页面请求后端404前端未配置反向代理Nginx中配置/api路径转发到后端服务端口如果你用宝塔面板部署强烈推荐直接上Docker容器。宝塔的Docker管理器可以一键创建SpringBoot容器环境隔离、迁移方便以后换服务器直接把镜像打包带过去不用重新配置环境。我的部署习惯是MySQL、Redis、后端服务各起一个容器再在宿主机配置Nginx反向代理。Docker部署时有个SpringBoot项目常见的坑容器内时间默认是UTC时区而数据库存的是北京时间查出来的时间总是差8小时。在启动容器时加-e TZAsia/Shanghai参数即可解决。另外容器内存限制要预留充足SpringBoot应用默认JVM堆内存可能吃掉宿主机大半内存用-e JAVA_OPTS-Xmx512m -Xms256m显式限制堆大小避免服务器资源耗尽。6. 深入理解SpringBoot自动配置原理6.1 为什么SpringBoot能自动配置做完整个项目你会对SpringBoot的自动配置机制产生天然的疑问为什么在配置类里写一个DataSource的Bean加上application.yml里的连接信息项目就能自动建好数据库连接池答案藏在spring-boot-autoconfigure包中。SpringBoot的自动配置核心机制是EnableAutoConfiguration注解它通过SpringFactoriesLoader机制加载META-INF/spring.factories文件中声明的所有自动配置类。这些配置类上都会加ConditionalOnClass、ConditionalOnMissingBean等条件注解。比如DataSourceAutoConfiguration只有在classpath存在javax.sql.DataSource类时才生效RedisAutoConfiguration只有在classpath存在RedisConnectionFactory时才生效。结合自动配置机制回答SpringBoot的starter是什么这类面试题时你的答案会特别有说服力starter本质上是一个Maven依赖组合包它把一组相关依赖和自动配置类打包在一起。引入spring-boot-starter-web后不仅有了SpringMVC相关jar包还自动配置好了DispatcherServlet、内置Tomcat、JSON序列化器等全套Web能力。你如果再讲一句这就是SpringBoot约定大于配置理念的体现面试官基本就能给你加分。6.2 自定义自动配置的场景项目中有一个很实际的场景适合自定义自动配置操作日志记录。每个Controller的写操作都想记录操作者、操作时间、操作内容与其在每个方法里手写日志代码不如用自定义注解加AOP切面统一处理。更进一步你还可以把这个切面逻辑封装成一个自定义starter放到公司内部仓库供多个项目复用。自定义一个starter需要做的事情很清晰写自动配置类标注Configuration和ConditionalOnClass相关条件在META-INF/spring.factories文件注册自动配置类在POM中声明spring-boot-autoconfigure-processor依赖生成配置元数据。我在项目中实践过把文件上传服务封装为独立模块定义一个FileStorageAutoConfiguration当项目配置了file.storage.local-path时自动装配本地存储实现类配置了file.storage.oss-endpoint时自动装配OSS存储实现类。切换到不同的云存储供应商时只需调整配置文件即可业务代码一行都不用改。这套思想来自SpringBoot的Profile机制和条件装配设计学透之后再看各种框架的扩展机制会有一种豁然开朗的感觉。7. 项目锦上添花的方向如果你时间充裕以下四个方向可以给项目增加真实亮点。一是引入Redis缓存热点数据。比如岗位列表是访问频率最高的接口每次查询都要打数据库。可以做一个简单的缓存策略岗位列表首次查询后存入Redis设置10分钟过期时间有新岗位发布时主动清除缓存下次查询重新加载。这个改造只需要一个缓存工具类加几行代码但答辩时讲缓存击穿、缓存穿透的应对方案会让项目的技术含金量上一个台阶。二是SpringBoot整合WebSocket实现消息实时推送。企业导师审核学生的实习申请后学生页面可以像聊天软件一样实时收到审核结果通知不必手动刷新页面。WebSocket的接入难度不大引入spring-boot-starter-websocket依赖实现一个WebSocketConfigurer配置类再开发一个消息推送服务类即可。需要注意的是WebSocket会话的管理和资源释放避免连接泄漏。三是Excel批量导入企业实习岗位信息。使用EasyExcel做一个模板文件企业用户在系统里下载模板、填写岗位信息、上传导入后端批量解析并校验数据合法性。这个功能在教学秘书批量导入学生信息、批量归档实习成果时也很实用。EasyExcel的API上手快文档齐全做导入导出的体验远好于Apache POI。批量导入时记得查询数据库是否存在重复数据通过唯一索引做幂等控制。四是引入SpringBoot Admin做运行监控。它将项目的运行状态可视化展示包括内存使用、线程池情况、请求耗时、健康状态等。只要引入依赖并做简单配置就能在浏览器里看到项目的实时运行指标。这个功能在自己本地调试和上线后排查问题时都有用。答辩时展示监控页面可以直观地向评委证明你的项目已经考虑了运维侧的能力。8. 个人经验总结我在实际开发这个项目时花在业务梳理上的时间比写代码多得多。很多人拿到这种题目就急着上手敲代码结果做到一半发现角色权限没想清楚、流程状态有漏洞又回头改表结构反复折腾浪费了大把时间。我的建议是开发前先把业务流程图和数据表结构画清楚用纸笔或在线工具花两个晚上把整个流程走一遍之后编码就是单纯的体力活。最后再分享一个小技巧你在答辩前把项目里用到的每个注解都翻一遍源码注释。面试官最喜欢问的就是你为什么在这个地方用Transactional不加的话会怎样RestController和Controller有什么区别这些细节都是加分项。系统做成什么样是能力的下限讲清楚为什么这么做才是能力的上限。