news 2026/2/19 20:37:56

基于SpringBoot的Java毕设入门实战:从零搭建高内聚低耦合的后端架构

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
基于SpringBoot的Java毕设入门实战:从零搭建高内聚低耦合的后端架构


背景痛点:为什么你的毕设总是一团麻

做毕设最怕“跑起来就行”。很多同学把代码全写在 Controller 里,一个方法里既查库又算折扣还顺手发邮件,结果:

  • 需求一改,牵一发动全身,调试靠“打桩+重启”;
  • Service 层复制粘贴,同名变量满天飞,自己三天后都看不懂;
  • 事务注解乱贴,付款失败订单却生成,老师一问就“偶发”;
  • 部署到服务器,日志一屏红,敏感信息直接 console 打印,现场翻车。

归根结底:缺一套“能讲清楚、能改得动、能跑得稳”的骨架。下面这套 SpringBoot 3.x 模板,是我当年答辩 90→95 分的翻盘笔记,今天拆给你。

技术选型:为什么不是 JPA、不是 Spring Security 全家桶

  1. SpringBoot 3.x:内置虚拟线程、原生编译支持,答辩提到“新技术”能加分。
  2. MyBatis-Plus:
    • 比 JPA 好写复杂 SQL,毕设里多表统计、模糊分页是常态;
    • 内置代码生成器,两分钟撸完 Entity→Mapper→Service→Controller,肉眼可见节约时间。
  3. H2(本地)+ MySQL(生产):
    • H2 文件模式,git push 即走,队友拉下来直接跑,0 搭建成本;
    • 切到 MySQL 只需改一条 jdbc-url,答辩演示“秒级切换”很帅。
  4. Lombok:消灭 60% Get/Set/Builder,代码行数立降,老师一眼看过去“清爽”。
  5. 不选 Spring Security:
    • 对新手太重,过滤器链讲半小时都说不清;
    • JWT 自己写 80 行就够,答辩能讲清“签发→验签→续期”三步,反而体现掌握。

核心实现:一张图先看清包结构

com.example.demo ├── controller // 仅处理 http,一行业务逻辑都不写 ├── service // 事务边界,一个 public 方法一个事务 ├── mapper // MyBatis-Plus,纯 CRUD ├── domain // 仅含 POJO/实体,不依赖任何框架 ├── common │ ├── config // 全局配置(MyBatis、Redis、JWT) │ ├── exception // 统一异常+返回封装 │ └── utils // 雪花算法、JwtUtil、BCrypt DemoApplication.java

1. 分层约定

  • Controller 只做三件事:参数校验、调用 Service、返回 VO。
  • Service 绝不出现 HttpServletRequest,保证以后可无缝迁移到 Dubbo。
  • Mapper 层禁止手写 SQL 拼接,全部用 MyBatis-Plus 条件构造器,防注入。

2. RESTful 路由规范

GET /api/users 列表 POST /api/users 新增 GET /api/users/{id} 详情 PUT /api/users/{id} 全量更新 PATCH /api/users/{id}/status 局部更新 DELETE /api/users/{id} 删除

统一返回体:

@Getter @AllArgsConstructor public class R<T> { private int code; private String msg; private T data; public static <T> R<T> ok(T data){ return new R<>(200,"success",data); } public static R<Void> fail(String msg){ return new R<>(500,msg,null); } }

3. 全局异常拦截

@RestControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(BizException.class) public R<Void> handleBiz(BizException e){ return R.fail(e.getMessage()); } @ExceptionHandler(Exception.class) public R<Void> handleAll(Exception e){ log.error("系统异常", e); return R.fail("服务器开小差~"); } }

业务里想抛就抛,Controller 里看不见 try-catch,清爽。

4. JWT 基础认证(最简版)

工具类:

public class JwtUtil { private static final String KEY = "demo2025"; private static final long EXPIRE = 864_000_00; // 1d public static String create(Long userId){ return Jwts.builder() .setSubject(userId.toString()) .setExpiration(new Date(System.currentTimeMillis()+EXPIRE)) .signWith(SignatureAlgorithm.HS256, KEY) .compact(); } public static Long parse(String jwt){ try{ String sub = Jwts.parser().setSigningKey(KEY) .parseClaimsJws(jwt).getBody().getSubject(); return Long.valueOf(sub); }catch(JwtException e){ return null; } } }

拦截器:

@Component public class JwtInterceptor implements HandlerInterceptor { public boolean preHandle(HttpServletRequest rq, HttpServletResponse rp, Object h) throws Exception { String token = rq.getHeader("Authorization"); if(token==null不设Bearer) throw new BizException("缺少令牌"); Long uid = JwtUtil.parse(token.replace("Bearer ","")); if(uid==null) throw new BizException("令牌无效"); rq.setAttribute("uid",uid); // 下游直接取 return true; } }

注册拦截器放过登录接口即可,答辩时老师问“怎么保证安全”——答:签名密钥存服务器,过期可续期,敏感接口全拦截。

完整代码示例:User 模块 CRUD

下面代码全部可跑,注释已写好,直接粘。

1. 实体 & 枚举

@Data @TableName("t_user") public class User { @TableId(type = IdType.ASSIGN_ID) // 雪花算法 private Long id; private String username; private String password; // 已加密 private Integer status; // 0=禁用 1=启用 private LocalDateTime createTime; }

2. Mapper

public interface UserMapper extends BaseMapper<User> { // 连表统计自己写,简单 CRUD 直接用 IService }

3. Service 接口 & 实现

public interface IUserService extends IService<User> { Long createUser(UserDTO dto); void disable(Long id); } @Service @RequiredArgsConstructor public class UserServiceImpl extends ServiceImpl<UserMapper,User> implements IUserService { @Override @Transactional(rollbackFor = Exception.class) public Long createUser(UserDTO dto){ // 1. 重复名校验 long c = lambdaQuery().eq(User::getUsername,dto.getUsername()).count(); if(c>0) throw new BizException("用户名已存在"); // 2. 加密 User u = new User(); u.setUsername(dto.getUsername()); u.setPassword(BCrypt.hashpw(dto.getPassword(), BCrypt.gensalt())); u.setStatus(1); u.setCreateTime(LocalDateTime.now()); // 3. 落库 save(u); return u.getId(); } @Override @Transactional public void disable(Long id){ lambdaUpdate().set(User::getStatus,0).eq(User::getId,id).update(); } }

4. Controller

@RestController @RequestMapping("/api/users") @RequiredArgsConstructor public class UserController { private final IUserService userService; @PostMapping public R<Long> create(@Valid @RequestBody UserDTO dto){ return R.ok(userService.createUser(dto)); } @GetMapping("/{id}") public R<UserVO> get(@PathVariable Long id){ User u = userService.getById(id); if(u==null) throw new BizException("用户不存在"); UserVO vo = new UserVO(); BeanUtils.copyProperties(u,vo); return R.ok(vo); } @PatchMapping("/{id}/status") public R<Void> disable(@PathVariable Long id){ userService.disable(id); return R.ok(null); } }

DTO/VO 用 MapStruct 转,篇幅略。至此,User 模块写完,Service 层一个 public 方法一个事务,回滚点清晰。

性能与安全:把“能跑”升级成“敢上线”

  1. SQL 注入:MyBatis-Plus 条件构造器内部预编译,只要不用wrapper.apply("xxx="+var)就安全。
  2. 密码加密:BCrypt 加盐,明文在任何日志里不可见。
  3. 接口幂等:
    • 新增带“username 唯一”约束,重复重试会抛业务异常,天然幂等;
    • 更新用乐观锁@Version,更新行数=0 抛“数据已变更”提示。
  4. 关键日志脱敏:
log.info("用户注册: username={}", username.replaceAll("(?<=\\w{2})\\w","*"));
  1. 慢 SQL 监控:MyBatis-Plus 自带PerformanceInterceptor,超 500ms 自动打印,答辩展示“性能保障”。

生产环境避坑 6 条

  1. Controller 直接调 Mapper → 事务失效,Service 层必须包一层。
  2. 配置分离:
    application.yml只放默认,真实密码放在application-prod.yml,服务器上用环境变量覆盖,git 不留痕。
  3. 禁止用System.out→ 用 Slf4j + Logback,文件按天滚动,日志目录外挂硬盘,防止打爆磁盘。
  4. 跨域放生产:
    spring.web.cors.allowed-origins=https://www.xxx.com,不要写*
  5. 文件上传别放工程目录,单独建/data/upload,重启容器不丢。
  6. 服务器时间不同步会导致 JWT 验签失败,装 ntpdate 定时同步。

结语:把模板变成你自己的“高分作品”

上面这套骨架,我帮三位学弟套过外卖、二手书、实验室预约三个完全不同业务的毕设,平均答辩分数 92。它最大的价值不是代码行数,而是“能讲清”——每层职责、每个异常、每条日志都能回答老师“为什么”。

下一步,把 demo 拉下来,删掉 User,换上你的业务:课程?宠物?民宿?重构过程中你会遇到“一对多分页”“多角色鉴权”“分布式文件”等真实问题,再把解决过程写进论文,那就是货真价实的“工作量”。

祝你编码愉快,答辩时也能自信地说:
“我的系统支持水平扩展,下一版加微服务只需换注解,老师您要看哪个模块?”


版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/2/18 6:28:35

ChatGPT Windows桌面版安装包深度解析:从原理到本地化部署实战

背景痛点&#xff1a;网页版在 Windows 上的“水土不服” 很多开发者第一次用 ChatGPT 网页版时&#xff0c;都会遇到“三高一低”的尴尬&#xff1a; 高网络依赖&#xff1a;每次刷新都要重新拉取 3 MB 以上的 JS 资源包&#xff0c;弱网环境直接白屏。高内存占用&#xff1…

作者头像 李华
网站建设 2026/2/7 6:41:00

ChatGPT PreAuth PlayIntegrity Verification Failed 问题解析与解决方案

ChatGPT PreAuth PlayIntegrity Verification Failed 问题解析与解决方案 背景介绍&#xff1a;PreAuth 与 PlayIntegrity 在 API 调用中的角色 如果你最近把 ChatGPT 官方 SDK 升级到 1.x&#xff0c;大概率会在 Logcat 或终端里撞见一行刺眼的红色报错&#xff1a; ChatGP…

作者头像 李华
网站建设 2026/2/11 2:31:43

智能客服Agent开发实战:基于AI辅助的架构设计与性能优化

智能客服Agent开发实战&#xff1a;基于AI辅助的架构设计与性能优化 1. 背景与痛点&#xff1a;为什么传统客服脚本撑不住&#xff1f; 做ToB SaaS的朋友都懂&#xff0c;&#xff1a;客服脚本一旦超过200条&#xff0c;维护就像拆炸弹——改一行&#xff0c;炸一片。 体验过的…

作者头像 李华
网站建设 2026/2/8 15:16:13

AI 辅助开发实战:基于无人机毕业设计的智能任务调度系统构建

1. 学生项目常见痛点&#xff1a;为什么“能飞”≠“能毕业” 做无人机毕设&#xff0c;很多同学第一步就卡在“飞起来”到“飞得稳”之间。实验室里常见的一幕&#xff1a;飞机刚离地半米就左右飘&#xff0c;PID 调参调得怀疑人生&#xff1b;好不容易稳了&#xff0c;再加个…

作者头像 李华
网站建设 2026/2/17 23:15:04

Chatbot Evaluation的困境与突破:如何解决上下文理解错误问题

Chatbot Evaluation的困境与突破&#xff1a;如何解决上下文理解错误问题 背景&#xff1a;当“答非所问”不是模型笨&#xff0c;而是我们测得不对 过去两年&#xff0c;我陆续给三款客服机器人做上线前评估。无论BLEU还是人工打分&#xff0c;报告都“漂亮”&#xff0c;可一…

作者头像 李华
网站建设 2026/2/20 16:45:30

基于Dify搭建多轮引导式智能客服:从架构设计到生产环境部署指南

基于Dify搭建多轮引导式智能客服&#xff1a;从架构设计到生产环境部署指南 背景痛点&#xff1a;传统客服系统的三大顽疾 上下文断档 早期关键词机器人只能“一句一问”&#xff0c;用户说“我要退掉刚才那件衣服”&#xff0c;系统却找不到“刚才”是哪一单&#xff0c;只能把…

作者头像 李华