tips: 先学习 ioc和di
一、前言
在前两篇中,我们已经完成:
Controller 接口开发
DTO 参数接收
@Valid 参数校验
但当前代码仍然存在明显问题:
Controller 承担了业务逻辑 ❌
返回结构不统一 ❌
没有异常处理 ❌
没有日志 ❌
👉 本篇将完成一次工程级升级
二、目标
我们将实现:
Controller → Service 分层 ✔
统一返回结构(Result)✔
统一异常处理 ✔
日志输出 ✔
三、统一返回结构(Result)
创建 Result 类
java
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;
}
}
👉 返回统一格式:
java
{
"code": 0,
"message": "成功",
"data": {}
}
四、Service 层设计
1️⃣ 接口
java
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️⃣ 实现类
java
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 重构
java
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 + 返回结果
六、统一异常处理(非常重要)
创建全局异常处理类
java
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());
}
}
👉 当异常发生:
java
{
"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)
👉 实现真实数据存储