当前位置: 首页 > news >正文

多用户跨学科交流系统(3):评论模块与 Spring Boot 全局异常处理

目录

  • 🧩评论模块(实体类、接口层、业务层等)
  • 🧩全局异常处理
  • 🥗修改之前的代码(主要是异常处理部分)
    • 1. ResponseEntity是啥?
    • 2. 改了哪些内容
  • 🥗可深入的点
  • 🥗面试问题
    • 一、Java / Spring Boot 异常基础
    • 二、Spring Boot 异常处理(核心)
    • 三、BusinessException(业务异常)
    • 四、ResponseEntity
    • 五、全局异常处理的写法
    • 六、进阶一点的题(你能答得很好)

🌻提前看
本篇博客继续基于前两篇博客,除了完成基础的评论模块,还对异常处理进行了深度解析和改动,使其尽量符合企业级项目。


🧩评论模块(实体类、接口层、业务层等)

基于MyBatis
1. Comment实体类

// src/main/java/com/example/blog/entity/Comment.java
package com.example.blog.entity;import lombok.Data;
import java.time.LocalDateTime;@Datapublic class Comment {private Long id;private Long post_id;private Long user_id;private Long parent_id;private String content;private LocalDateTime created_at;
}

2. CommentMapper (接口层)

// src/main/java/com/example/blog/mapper/CommentMapper.javapackage com.example.blog.mapper;import com.example.blog.entity.Comment;
import org.apache.ibatis.annotations.*;
import java.util.List;@Mapper
public interface CommentMapper {// 插入评论@Insert("""INSERT INTO comment (post_id, user_id, content, created_at)VALUES (#{post_id}, #{user_id}, #{content}, #{created_at})""")@Options(useGeneratedKeys = true, keyProperty = "id")int insert(Comment comment);// 根据文章ID查询评论@Select("SELECT * FROM comment WHERE post_id = #{post_id} ORDER BY created_at DESC")List<Comment> findByPostId(Long post_id);// 删除评论@Delete("DELETE FROM comment WHERE id = #{id}")int deleteById(Long id);
}

3. CommentService(业务层)

// src/main/java/com/example/blog/service/CommentService.javapackage com.example.blog.service;import com.example.blog.entity.Comment;
import com.example.blog.mapper.CommentMapper;
import org.springframework.stereotype.Service;import java.time.LocalDateTime;
import java.util.List;@Service
public class CommentService {private final CommentMapper commentMapper;public CommentService(CommentMapper commentMapper) {this.commentMapper=commentMapper;}public void addComment(Comment comment) {comment.setCreated_at(LocalDateTime.now());commentMapper.insert(comment);}public List<Comment> getCommentsByPostId(Long postId) {return commentMapper.findByPostId(postId);}public void deleteComment(Long id) {commentMapper.deleteById(id);}
}

4. CommentController (接口层)

// src/main/java/com/example/blog/controller/CommentController.javapackage com.example.blog.controller;import com.example.blog.entity.Comment;
import com.example.blog.entity.Result;
import com.example.blog.service.CommentService;
import org.springframework.web.bind.annotation.*;import java.util.List;@RestController
@RequestMapping("/comments")
public class CommentController {private final CommentService commentService;public CommentController(CommentService commentService){this.commentService=commentService;}//添加评论@PostMappingpublic Result<String> addComment(@RequestBody Comment comment){commentService.addComment(comment);return Result.success("评论发布成功");}//获取某篇文章下的评论@GetMapping("/post/{post_id}")public List<Comment> getComments(@PathVariable Long post_id){return commentService.getCommentsByPostId(post_id);}//删除评论@DeleteMapping("/{id}")public Result<String> deleteComment (@PathVariable Long id) {commentService.deleteComment(id);return Result.success("删除成功");}
}

🧩全局异常处理

1. 创建全局异常处理器
只要你的 Controller / Service 里抛出异常,这里会自动拦截下来并输出统一格式。

// 新建包和类
// com.example.blog.exception.GlobalExceptionHandlerpackage com.example.blog.exception;import com.example.blog.entity.Result;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;@RestControllerAdvice
public class GlobalExceptionHandler {//处理所有未捕获异常@ExceptionHandler(Exception.class)public Result<?> handleException(Exception e) {return Result.error("服务器内部错误"+e.getMessage());}// 处理参数非法异常@ExceptionHandler(IllegalArgumentException.class)public Result<?> handleArgument(IllegalArgumentException e) {return Result.error(e.getMessage());}//其他异常类型}

2. 测试一下
在任意 Controller 写一个测试方法:

@GetMapping("/test/error")
public String testError() {throw new IllegalArgumentException("参数不合法!");
}

在这里插入图片描述

3. 官方文档+高质量教程

  • Spring Framework — Exception handling 部分(官方): https://docs.spring.io/spring-framework/reference/web/webmvc/mvc-ann-rest-exceptions.html
  • Spring MVC — @ExceptionHandler / @ControllerAdvice 机制(官方): https://docs.spring.io/spring-framework/reference/web/webmvc/mvc-controller/ann-exceptionhandler.html
  • 教程文章:“Get Started with Custom Error Handling in Spring Boot” — 实战角度讲得不错: https://auth0.com/blog/get-started-with-custom-error-handling-in-spring-boot-java/

🥗修改之前的代码(主要是异常处理部分)

我们之前异常返回用的Result,现在改用ResponseEntity

1. ResponseEntity是啥?

一句话:
它是 Spring 用来返回 HTTP 响应的万能容器。

  • 你可以用它同时设置:
    HTTP 状态码(200、400、404、500……)
    响应头 header
    响应体 body(你真正要返回的数据)

  • 不像之前用的 Result<?>,只能返回 body。

  • 写法举例:

return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(Result.error("用户名不存在"));

这样浏览器看到的 HTTP 状态码是 400 Bad Request
更符合 REST 风格,也更利于前端判断。

2. 改了哪些内容

  1. Result 实体类中 返回错误 函数增加 code 参数。
  2. 新增BusinessException类
// com/blog/exception/BusinessExceptionpackage com.example.blog.exception;public class BusinessException extends RuntimeException {private int code;public BusinessException(int code,String msg) {super(msg);this.code = code;}public int getCode() {return code;}
}
  1. 修改异常全局处理器中的内容
package com.example.blog.exception;import com.example.blog.entity.Result;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;@RestControllerAdvice
public class GlobalExceptionHandler {// 业务异常@ExceptionHandler(BusinessException.class)public ResponseEntity<Result<?>> handleBusinessException(BusinessException e) {return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(Result.error(e.getCode(), e.getMessage()));}// 参数非法异常@ExceptionHandler(IllegalArgumentException.class)public ResponseEntity<Result<?>> handleIllegalArgument(IllegalArgumentException e) {return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(Result.error(400, "参数不合法:" + e.getMessage()));}// 兜底异常@ExceptionHandler(Exception.class)public ResponseEntity<Result<?>> handleException(Exception e) {e.printStackTrace(); // ⭐ 打印异常栈,方便排查return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(Result.error(500, "服务器内部错误"));}
}
  1. 在业务代码遇到业务错误时抛出这个异常
    我主要改了UserService里面的内容,连带UserController返回类型也要改(其他模块的自行去改)
// UserServicepackage com.example.blog.service;import com.example.blog.entity.User;
import com.example.blog.exception.BusinessException;
import com.example.blog.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;@Service
public class UserService {@Autowiredprivate UserMapper userMapper;private final BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();// 💡 注册:加密密码再存数据库public void register(User user) {//用户名重复if(userMapper.findByUsername(user.getUsername())!=null) {throw new BusinessException(1001,"用户名已存在");}user.setPassword(encoder.encode(user.getPassword()));user.setRole("user");int rows = userMapper.insert(user);if(rows!=1) {throw new BusinessException(1002,"注册失败,请稍后再试");}}// 💡 登录:验证用户名存在 + 密码匹配public User login(String username, String rawPassword) {User dbUser = userMapper.findByUsername(username);if(dbUser == null) {throw new BusinessException(1003,"用户不存在");}if(!encoder.matches(rawPassword,dbUser.getPassword())) {throw new BusinessException(1004,"密码错误");}return dbUser;}
}
//Controller/UserControllerpackage com.example.blog.controller;import com.example.blog.entity.Result;
import com.example.blog.entity.User;
import com.example.blog.service.UserService;
import com.example.blog.utils.JwtUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;@RestController
@RequestMapping("/user")
public class UserController {@Autowiredprivate UserService userService;// 💡 注册接口@PostMapping("/register")public Result<?> register(@RequestBody User user) {userService.register(user);return Result.success("注册成功");}// 💡 登录接口@PostMapping("/login")public Result<?> login(@RequestBody User user) {User dbUser = userService.login(user.getUsername(), user.getPassword());// 登录成功,签发TokenString token = JwtUtil.generateToken(dbUser.getUsername());return Result.success(token);}@GetMapping("/test/error")public String testError() {throw new IllegalArgumentException("参数不合法!");}
}

🥗可深入的点

  1. Controller 层入参校验(@Valid) + 自动异常处理
  2. 统一返回体规范化
  3. 日志系统(Logback)
  4. 全链路错误码体系

🥗面试问题

我学的这部分内容在面试里主要考察对“异常体系”和“REST API 返回规范”的理解。
重点包括:全局异常处理、异常分类、业务异常设计、ResponseEntity 使用、错误码体系。
这些内容是实习项目中比较“工程化”的能力,和写增删改查的 Controller 拉开差距。

一、Java / Spring Boot 异常基础

1. 异常分为哪两大类?区别是什么?

答案:

  • 受检异常(Checked Exception):必须 try/catchthrows,如 IOException、SQLException。
  • 非受检异常(Unchecked Exception):不强制处理,RuntimeException 及其子类,如 NullPointerException。 区别:编译期是否要求必须处理。

2. RuntimeException 为什么不用强制捕获?

答案: 因为它表示的是程序逻辑错误,不是外部环境问题。 强制捕获反而会让代码臃肿。

3. throw 和 throws 的区别?

答案:

  • throw:在方法内部抛异常对象
  • throws:声明方法可能抛出某类异常

二、Spring Boot 异常处理(核心)

4. 什么是全局异常处理?为什么要用?

答案: 使用 @RestControllerAdvice + @ExceptionHandler 统一捕获项目所有异常。 好处:

  1. 错误返回格式统一
  2. 代码更简洁(不用每个 Controller 写 try/catch)
  3. 便于维护和日志记录

5. @ControllerAdvice 和 @RestControllerAdvice 的区别?

答案:

  • @ControllerAdvice:返回视图(需要 @ResponseBody 才变成 JSON)
  • @RestControllerAdvice:默认返回 JSON,更适合 REST API

6. @ExceptionHandler 的作用是什么?

答案:

指定方法用来处理某一种(或一类)异常,比如:

@ExceptionHandler(IllegalArgumentException.class)

7. @ExceptionHandler 会按顺序匹配吗?

答案: 不会按代码顺序匹配,而是按异常类型的精确程度。 比如 IllegalArgumentException 会比 Exception 优先命中。

三、BusinessException(业务异常)

8. 业务异常(BusinessException)是用来干嘛的?

答案: 用来处理“业务逻辑相关的错误”,比如:

  • 用户不存在
  • 密码错误
  • 文章已删除
  • 权限不足

表现方式:

是正常业务流程里可能出现的可预期错误,而不是程序bug。

9. 为什么要定义 BusinessException,而不是直接用 RuntimeException?

答案:

  • 可以带业务错误码
  • 前后端沟通更清晰
  • 更方便统一处理
  • 区分系统错误(500)和业务错误(400,401,403 等)

10. BusinessException 通常包含哪些字段?

答案:

  • 错误码(int)
  • 错误信息(String)

比如:

throw new BusinessException(1003, "用户不存在");

四、ResponseEntity

11. ResponseEntity 是什么?为什么要用它?

答案: 它是 Spring 用来构建 HTTP 响应的对象,可以同时设置:

  • 状态码(如 200/400/500)
  • 响应体(body)
  • 响应头(headers)

适合严格遵守 REST 风格的项目。

12. ResponseEntity 与直接返回对象的区别?

答案:

方式能否设置状态码能否设置 header常见场景
return 对象❌ 不行❌ 不行小项目,简单 JSON API
ResponseEntity✔ 可以✔ 可以企业、RESTful API

五、全局异常处理的写法

13. 你写过一个全局异常处理类,它主要处理哪些类型?

答案示例:

  • BusinessException(业务异常 → 400)
  • IllegalArgumentException(参数非法 → 400)
  • Exception(兜底异常 → 500)

并返回统一格式的 Result

14. 为什么兜底异常要打印 e.printStackTrace?

答案: 因为兜底异常通常是系统 bug,不打印日志会导致你无法排查问题。

15. 实际开发中,IllegalArgumentException 什么时候会出现?

答案示例:

  • 手动检查参数
  • 对象为空
  • 传入非法数值
  • 自己 throw new IllegalArgumentException("xxx")

六、进阶一点的题(你能答得很好)

16. 你如何设计一套错误码体系?

简单答案(面试官会觉得你理解了):

系统级错误与业务级错误分开管理,例如:

  • 5000~5999:系统异常
  • 1000~1999:用户模块错误
  • 2000~2999:文章模块错误
  • 3000~3999:评论模块错误
    并在 BusinessException 中使用统一的字段返回。

17. 为什么全局异常类里面不要大量捕获具体异常?

答案:

  • 太细会使代码难维护
  • 优先只处理常见的(业务、参数、系统)即可
  • 其他异常可以在日志系统中追踪
http://www.dtcms.com/a/613881.html

相关文章:

  • 前后端分离部署学之思在线考试系统
  • 在树莓派4B上部署ONNX格式的YOLOv8-Pose
  • websocket和传统socket有何差别?是属于一个子集还是完全不同?
  • 双指针问题2(c++)
  • 龙岩市城乡规划建设局网站三只松鼠的网络营销方式
  • Docker容器使用手册——入门篇(上)
  • C语言编译器网页版 | 轻松编写与调试C语言程序
  • QT注册自定义类相关的两个用法
  • 进程间关系(linux)
  • WordPress外贸成品网站的免费获取渠道
  • 经典算法题之汉明距离(二)
  • 公司注册网站模板上杭网站设计
  • 基于Spring AI的RAG和智能体应用实践
  • Flutter:跨平台开发终极指南
  • Python-将身份证正反面图片-生成PDF
  • 建单页网站搜网站的关键词
  • 数据科学每日总结--Day20--区块链
  • 自建网站平台有哪些建立个人网站能干
  • Docker中容器的备份方法和步骤
  • 折叠屏手机如何选:横向内折与竖向内折形态及核心参数解析
  • 我想网上做网站软件项目管理书籍推荐
  • el-table组件右侧出现空隙
  • 南宁响应式网站制作抖音运营公司排名前十
  • 【IO模型与并发服务器】
  • QT QML Item基类浅谈
  • Go语言学习笔记(二)
  • 前端CSS预处理器对比,Sass与Less
  • Ubuntu NAT模式设置静态 IP 地址
  • 建英文网站广州排名seo公司
  • Qt 对 JSON和XML文件的操作详解