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

Spring Boot 实战(三):Service 分层 + 统一返回 + 异常处理(工程级写法)

tips: 先学习 ioc和di

Spring 核心思想解析:IoC 与 DI 一文讲透(从入门到工程理解)

一、前言

在前两篇中,我们已经完成:

Controller 接口开发 DTO 参数接收 @Valid 参数校验

但当前代码仍然存在明显问题:

Controller 承担了业务逻辑 ❌ 返回结构不统一 ❌ 没有异常处理 ❌ 没有日志 ❌

👉 本篇将完成一次工程级升级


二、目标

我们将实现:

Controller → Service 分层 ✔
统一返回结构(Result)✔
统一异常处理 ✔
日志输出 ✔

三、统一返回结构(Result)

创建 Result 类

package org.example.arkbackend.common; import lombok.Data; @Data public class Result<T> { private int code; private String message; private T data; public static <T> Result<T> success(T data) { Result<T> r = new Result<>(); r.setCode(0); r.setMessage("成功"); r.setData(data); return r; } public static <T> Result<T> fail(String message) { Result<T> r = new Result<>(); r.setCode(1); r.setMessage(message); return r; } }

👉 返回统一格式:

{ "code": 0, "message": "成功", "data": {} }

四、Service 层设计

1️⃣ 接口

package org.example.arkbackend.service; import org.example.arkbackend.dto.UserRegisterDTO; public interface UserService { String register(UserRegisterDTO dto); String getUserById(Long id); void deleteUser(Long id); }

2️⃣ 实现类

package org.example.arkbackend.service.impl; import lombok.extern.slf4j.Slf4j; import org.example.arkbackend.dto.UserRegisterDTO; import org.example.arkbackend.service.UserService; import org.springframework.stereotype.Service; @Slf4j @Service public class UserServiceImpl implements UserService { @Override public String register(UserRegisterDTO dto) { log.info("开始注册用户:{}", dto.getUsername()); // 模拟业务逻辑 if ("admin".equals(dto.getUsername())) { throw new RuntimeException("用户名已存在"); } return "注册成功:" + dto.getUsername(); } @Override public String getUserById(Long id) { log.info("查询用户ID:{}", id); return "用户ID:" + id; } @Override public void deleteUser(Long id) { log.info("删除用户ID:{}", id); } }

五、Controller 重构

package org.example.arkbackend.controller; import jakarta.validation.Valid; import org.example.arkbackend.common.Result; import org.example.arkbackend.dto.UserRegisterDTO; import org.example.arkbackend.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; @RestController @RequestMapping("/user") public class UserController { @Autowired private UserService userService; @PostMapping("/register") public Result<String> register(@Valid @RequestBody UserRegisterDTO dto) { return Result.success(userService.register(dto)); } @GetMapping("/{id}") public Result<String> getUser(@PathVariable Long id) { return Result.success(userService.getUserById(id)); } @DeleteMapping("/{id}") public Result<Void> delete(@PathVariable Long id) { userService.deleteUser(id); return Result.success(null); } }

👉 Controller 现在只负责:

接收参数 + 调用 Service + 返回结果

六、统一异常处理(非常重要)

创建全局异常处理类

package org.example.arkbackend.exception; import org.example.arkbackend.common.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.fail(e.getMessage()); } }

👉 当异常发生:

{ "code": 1, "message": "用户名已存在" }

七、日志(Log)

使用 @Slf4j

@Slf4j

使用方式

log.info("注册用户:{}", username); log.error("发生异常:{}", e.getMessage());

👉 优点:

✔ 可追踪 ✔ 可定位问题 ✔ 可分析行为

八、完整执行流程

请求进入 Controller → 参数校验(@Valid) → 调用 Service → Service 执行业务逻辑 → 返回 Result → 异常统一处理

九、当前项目结构

controller/ dto/ service/ service/impl/ common/ exception/

👉 已具备:

企业级基础架构 ✔

十、一句话总结

通过 Service 分层、统一返回结构和全局异常处理,可以将简单接口升级为具备工程规范的后端系统

下一篇:

Spring Boot 实战(四):MySQL + MyBatis 接入,打通用户注册最小闭环

数据库(MySQL) + 持久层(Mapper / JPA)

👉 实现真实数据存储

http://www.cnnetsun.cn/news/1930190.html

相关文章:

  • FigmaCN终极指南:3分钟实现Figma中文界面汉化的完整教程
  • 新手必看:Ubuntu22.04下Wine安装微信实战,彻底消除窗口阴影置顶问题
  • 不用仿真器也能玩转STM32?手把手教你用CH340模块实现串口ISP烧录
  • 终极罗技鼠标宏指南:5分钟实现绝地求生零后坐力压枪
  • Hyperf对接报表 企业级报表系统中,针对百万级数据量的帆布报表导出场景,请从 HyperF 的进程模型、内存管理、分页查询三个维度,设计一套完整的性能优化方案。
  • Pytorch——momentum动量:从物理直觉到梯度下降的“惯性”加速
  • Halcon实战:用两种方法搞定XLD轮廓中线提取(附完整代码)
  • 【Hot 100 刷题计划】 LeetCode 72. 编辑距离 | C++ 经典 DP 增删改状态转移
  • 终极Windows PDF处理方案:Poppler预编译包完整指南
  • 5G入网第一步:手把手拆解PBCH信道里的MIB消息(附LTE/NR对比)
  • Oracle EBS 自然科目段设计方案 + SAP 与 EBS 科目映射对照表
  • 基于STM32F103的3.6kW全桥逆变器资料集:并网充电放电、自动切换及全方位保护
  • AI电商详情页生成落地指南(SITS2026内部验证版):5类高危失效场景+4个不可绕过的合规校验点
  • 从CTF题目到实战:UPX脱壳中的OEP定位技巧与经验分享
  • python中海龟绘图之海龟印章
  • 告别版本不兼容!用Python脚本+ArcGIS工具箱,5分钟搞定上百个mxd文件批量降级
  • FigmaToCode终极指南:如何5分钟将设计稿转换为多平台代码
  • 别再只用SELECT了!解锁MySQL Handler接口,像操作文件一样‘打开-读取-关闭’数据表
  • MyBatis 面试连环炮:从源码原理到实战避坑,彻底拿下 Offer 通关秘籍
  • 如何在Navicat中使用逻辑模型转为物理模型_架构师必备技能
  • 深度学习基于YOLOv8的苹果叶片病害检测系统 Python + PyQt5可视化界面 苹果病害检测数据集的训练及应用
  • UVM验证平台组件通信避坑指南:从TLM端口连接到uvm_config_db的正确用法
  • 用立创EDA复刻FM5324GA充放电模块,做个雪花灯底座给娃当礼物(附完整BOM清单)
  • 有机化学中苯环上取代基的次序规则
  • 手把手教你用STM32F103C8T6和MAX30102做个心率血氧仪(附完整代码和算法解析)
  • 如何用UABEA轻松修改Unity游戏资源?新手完整指南
  • 生成式AI多语言支持实战手册(覆盖127种语系+低资源语言破局方案)
  • vSphere 6.7证书突然过期,凌晨三点我这样救活了vCenter
  • 工业肌肉:04 机器人为什么能精准到 0.01mm?运动控制三参数告诉你
  • 告别环境配置噩梦:用PothosSDR 2020.01.26一站式搞定Windows下的USRP开发环境