前后端分离下,Spring Boot 请求从发起到响应的完整执行流程

news2025/4/9 3:32:46

以下是前后端分离架构下,Spring Boot 请求从发起到响应的完整执行流程,结合你提出的所有问题,按真实执行顺序和职责链条重新整理所有核心概念、结构、关键类、数据转换点和典型代码示例:


一、前端发起请求(步骤1-2)

关键组件:React/Vue + Axios + JSON

axios.get('/api/users', { headers: { Authorization: 'Bearer xxx' } });
  • 不会包含 JSON body(因 GET 请求规范所限)

  • 请求参数以 query param 附带,如 /api/users?active=true


二、后端接收请求与过滤(步骤3)

关键组件:Spring Security Filter Chain

public class JwtAuthenticationFilter extends OncePerRequestFilter {
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) {
        String header = request.getHeader("Authorization");
        String token = header != null && header.startsWith("Bearer ") ? header.substring(7) : null;
        if (token != null && jwtUtil.validate(token)) {
            Authentication auth = jwtUtil.getAuthentication(token);
            SecurityContextHolder.getContext().setAuthentication(auth);
        }
        chain.doFilter(request, response);
    }
}
  • JWT 验证只发生在 Filter 中,不会进入 Controller 就已判定是否通过

  • 验证通过后构造 Authentication 对象放入 SecurityContext


三、DispatcherServlet 分发请求(步骤4)

核心作用:统一接收请求,协调执行链

  • DispatcherServlet 并不执行具体处理,只负责流程编排


四、HandlerMapping 确定 Handler(步骤5)

RequestMappingHandlerMapping:匹配@Controller + @RequestMapping 控制器方法
  • 负责查找哪个 Controller 的哪个方法应该处理这个请求

  • 构建好 HandlerExecutionChain(包含拦截器)


五、HandlerAdapter 调用处理器(步骤6)

RequestMappingHandlerAdapter:负责调用注解控制器方法
  • supports(Object handler) 方法确认适配器是否支持此处理器

  • 参数解析、类型转换、方法调用都由它完成


六、Controller 执行业务分发(步骤7)

@RestController
@RequestMapping("/api/users")
public class UserController {
    @Autowired
    private UserService userService;

    @GetMapping
    public ResponseEntity<List<UserDto>> getUsers() {
        List<UserDto> list = userService.getAllUsers();
        return ResponseEntity.ok(list);
    }
}
  • Controller 不应处理认证逻辑,而是专注于业务与 DTO 构造

  • 使用 ResponseEntity 是为了更灵活控制 HTTP 状态码与头部


七、Service 层执行业务逻辑(步骤8)

@Service
public class UserServiceImpl implements UserService {
    public List<UserDto> getAllUsers() {
        List<User> entities = userRepository.findAll();
        return entities.stream().map(this::toDto).toList();
    }

    private UserDto toDto(User user) {
        return new UserDto(user.getId(), user.getName(), user.getEmail());
    }
}
  • 接收 Entity 对象 → 转换为 DTO(数据传输对象)

  • 不应返回 ResponseEntity,这保持了关注点分离


八、Repository 访问数据库(步骤9-11)

@Repository
public interface UserRepository extends JpaRepository<User, Long> {
    List<User> findByActiveTrue();
}

@Entity
@Table(name = "users")
public class User {
    @Id
    private Long id;
    private String name;
    private String email;
}
  • DAO 实质上是 Repository 接口本身(即 Data Access Object)

  • Entity 是 ORM 框架(JPA/Hibernate)使用的 Java → 表 的映射结构

  • 从数据库返回的是 Entity 实例,非 DTO,非 DAO


九、响应流程(步骤12-17)

数据返回结构:

步骤返回对象类型
12Repository → ServiceEntity 实体类
13Service → ControllerDTO(如 UserDto)
14Controller → HandlerAdapterResponseEntity 或直接 DTO
15HandlerAdapter → DispatcherServlet包装为 ModelAndView(内部结构)
17DispatcherServlet → 前端JSON 数据(通过 HttpMessageConverter 序列化)

十、常见问题澄清汇总

❓JWT是谁生成的?前端能编辑它吗?

  • ❌ 前端不能生成 JWT,必须由后端签名生成

  • ✅ JWT 中的 claim 字段(sub/iss/exp)由后端控制

❓为什么需要 Authentication?不能直接验证放行?

  • ✅ 因为权限注解(如 @PreAuthorize)等依赖 SecurityContextHolder 中的 Authentication 对象

❓HandlerMapping vs HandlerAdapter 为什么都要有?

组件功能是否必须
HandlerMapping确定哪个 Controller 处理请求
HandlerAdapter执行该 Controller 的方法
  • 二者实现了解耦:DispatcherServlet 不直接依赖 Controller 类型

  • 实际开发可以注册自定义 Mapping/Adapter

❓ResponseEntity vs DTO vs ResponseObject 区别?

类型

定义

用途

DTO

纯Java对象,字段只为数据交换

Service 返回给 Controller

ResponseEntity

Spring类,用于封装响应状态、头、体

Controller 返回给框架

自定义 ResponseObject

如 ApiResponse,封装统一格式

可选嵌入于 ResponseEntity 中

不是必须使用 ResponseEntity,直接返回 DTO,Spring 也会自动序列化


建议实践结构总结

前端请求 → Filter链认证(JWT) → DispatcherServlet协调 → HandlerMapping确定Controller
→ HandlerAdapter调用Controller方法 → Controller调用Service → Service调用Repository
→ Repository返回Entity → Service转换为DTO → Controller包装为ResponseEntity
→ 返回给前端(JSON)


✅ 先明确三个概念:

注解

用于

描述

@ExceptionHandler

控制器或异常处理类中的方法

声明某个方法用来处理特定类型异常

@ControllerAdvice

类级别

用于集中定义所有控制器的全局异常处理

@RestControllerAdvice

@ControllerAdvice + @ResponseBody

自动将返回值作为JSON响应


✅ 情况分类:异常处理的三个层级

异常发生层

谁处理

示例

Controller层内部异常

当前 Controller 类中的 @ExceptionHandler@ControllerAdvice

请求参数缺失、非法访问

Service层抛出业务异常

交由 Controller 接收并传播给 @ControllerAdvice

用户名重复、余额不足

Repository层抛出数据访问异常

Service 可以选择 catch/转义,也可以上抛

JPA抛出 DataIntegrityViolationException

❓1. “用了@ExceptionHandler,为什么还需要@ControllerAdvice?”

  • @ExceptionHandler 是方法级,@ControllerAdvice 是全局级的,集中管理多个Controller的异常处理 。所以两者不是“互相替代”,而是范围不同:ControllerAdvice 是容器,ExceptionHandler 是容器里的处理器方法


❓2. 那为什么还说“Controller处理Service抛出的异常”?“Controller处理Service异常”指的是:“异常发生于Service层,但由Controller层抛出并触发了统一异常处理机制”。


✅ 代码级举例(完整调用链)

➤ 1. 自定义异常类:

➤ 2. Service层抛出异常:

➤ 3. Controller调用Service:

➤ 4. 全局异常处理器:

@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(UserAlreadyExistsException.class)
    public ResponseEntity<ApiError> handleUserExists(UserAlreadyExistsException ex) {
        ApiError error = new ApiError(409, ex.getMessage());
        return ResponseEntity.status(HttpStatus.CONFLICT).body(error);
    }

    @ExceptionHandler(Exception.class)
    public ResponseEntity<ApiError> handleOther(Exception ex) {
        ApiError error = new ApiError(500, \"Internal server error\");
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(error);
    }
}

✅ 小结(极简结论):

问题结论
@ExceptionHandler@ControllerAdvice 冲突吗?❌ 不冲突,前者方法级,后者类级,全局异常需要配合使用
Controller是否“处理”Service抛出的异常?✅ 表面上Controller收到异常,实则由 @ControllerAdvice 捕获
实际开发该怎么用?✅ 建议所有 Controller 异常集中交由 @RestControllerAdvice 来统一处理

✅ 一、前后端通信中的数据结构与序列化处理

1. 前端发送 GET 请求 /api/users,不做 JSON 清洗吗?后端收到的是什么?直接处理 JSON 吗?

  • ✅ GET 请求不会带有 JSON 请求体(规范禁止),数据清洗体现在构造 Query 参数(如 /api/users?active=true)。

  • ✅ 后端不会处理 JSON,而是通过 @RequestParam 解析 URL 查询参数。

  • ✅ JSON 序列化只发生在 POST/PUT 请求体响应数据 中。


2. JSON 序列化 / 反序列化发生在哪一步?DispatcherServlet 负责吗?

  • ✅ 反序列化(JSON → Java)发生在 HandlerAdapter 调用 Controller 方法前

  • ✅ 序列化(Java → JSON)发生在 Controller 方法返回后

  • DispatcherServlet 不负责 JSON 处理,它只负责请求转发。

  • ✅ JSON 的处理由 HttpMessageConverter(默认使用 Jackson)完成。


✅ 二、DAO / DTO 的职责与流程中的定位

3. 什么是 DAO 和 DTO?图中的第 12~14 步分别返回的是什么?

  • ✅ DAO(Data Access Object)是用于封装数据库访问逻辑的接口,如 UserRepository extends JpaRepository

  • ✅ DTO(Data Transfer Object)是前后端或多层之间传递数据的载体,如 UserDto

  • 图中:

    • 第12步:DAO 返回的是实体类(Entity);

    • 第13步:Service 转换为 DTO,返回给 Controller;

    • 第14步:Controller 使用 ResponseEntity<DTO> 作为响应,框架底层封装为 ModelAndView


4. ResponseEntity 和 Model 有什么区别?第 15 步为什么是 ModelAndView?

  • ResponseEntity 用于前后端分离,返回完整 JSON 响应;

  • Model 是传统 MVC 模式中用于传数据给视图模板的结构;

  • ModelAndView 是 Spring MVC 底层用于封装所有结果的统一结构(即使你返回的是 ResponseEntity,框架也封装为 ModelAndView(null, body))。

  • ✅ 前后端分离时,开发者只用 ResponseEntity,无需直接接触 ModelAndView


✅ 三、Spring Security 中的 JWT 验证机制

5. JWT 验证流程在 Spring Boot 中是怎样的?在哪一层?

  • ✅ JWT 验证发生在 FilterChain 阶段,通常在 OncePerRequestFilter 中实现;

  • ✅ 验证步骤:

    • 从请求 Header 获取 Authorization: Bearer <token>

    • 验证签名、结构、过期时间;

    • 解析生成 Authentication

    • 设置到 SecurityContextHolder

    • 放行到 DispatcherServlet;

  • ❌ DispatcherServlet 和 Handler 不参与认证,只接收已验证请求。


6. JWT 是由前端编辑生成的吗?字段如 iss、sub、exp、roles 是前端决定的吗?

  • ❌ JWT 不能由前端生成;

  • ✅ JWT 是后端使用密钥生成的,包含的字段完全由后端控制;

  • ✅ 前端只是接收、存储并在请求中携带;

  • JWT 示例字段如 sub: userId, exp: timestamp, roles: ["ADMIN"] 都由后端代码设置。


7. 为什么 JWT 验证完还要构建 Authentication 对象?不能直接转发请求吗?

  • ✅ 构建 Authentication 的目的是让 Spring Security 安全机制生效:

    • 权限判断(如 @PreAuthorize)依赖 SecurityContext

    • 日志、审计、日志追踪、用户上下文等都依赖它;

  • ❌ 如果跳过该步骤,系统将视为匿名用户,请求将不具备认证身份。


✅ 四、Spring Security 的方法级授权机制

8. @PreAuthorize 注解用在哪些类?在哪一步执行?为什么叫 Pre?看起来像是在认证前

  • @PreAuthorize 用于 Controller 或 Service 方法上,限制某角色、用户是否能执行该方法;

  • ✅ 执行时机是 在方法体执行之前,不是在登录认证之前;

  • ✅ 所谓“Pre”指的是在方法调用前进行权限判断

  • ✅ 注解本质通过 AOP 拦截方法,依赖于 Authentication 已存在(由 Filter 中设置);

  • ❌ 如果没有 Authentication@PreAuthorize 判断失效,默认拒绝访问。

用法:

@PreAuthorize("hasRole('ADMIN')")
public void deleteUser(Long id) { ... }

@PreAuthorize("#userId == authentication.name")
public void updateUser(Long userId, UserDto dto) { ... }

✅ 总结关键词表

关键词解释
DTO传输数据结构体,跨层/跨系统使用
DAO数据访问接口,用于封装数据库操作
ResponseEntity用于返回 JSON 响应体的 Spring 结构
ModelAndViewSpring MVC 内部统一封装结构
JWTJSON Web Token,认证令牌,由后端生成
AuthenticationSpring Security 中用于表示认证用户的对象
SecurityContextHolder保存当前请求上下文的认证信息
@PreAuthorize方法级授权注解,执行方法前做权限判断

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2329578.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【文献阅读】Vision-Language Models for Vision Tasks: A Survey

发表于2024年2月 TPAMI 摘要 大多数视觉识别研究在深度神经网络&#xff08;DNN&#xff09;训练中严重依赖标注数据&#xff0c;并且通常为每个单一视觉识别任务训练一个DNN&#xff0c;这导致了一种费力且耗时的视觉识别范式。为应对这两个挑战&#xff0c;视觉语言模型&am…

spring-cloud-alibaba-nacos-config使用说明

一、核心功能与定位 Spring Cloud Alibaba Nacos Config 是 Spring Cloud Alibaba 生态中的核心组件之一&#xff0c;专为微服务架构提供动态配置管理能力。它通过整合 Nacos 的配置中心功能&#xff0c;替代传统的 Spring Cloud Config&#xff0c;提供更高效的配置集中化管理…

C# Winform 入门(9)之如何封装并调用dll

封装dll 首先创建 .Net平台 类库 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks;namespace _09.Encapsulation_dll {public class Program{/// <summary>/// 求两个double类型的数值的和/// &l…

【嵌入式系统设计师】知识点:第2章 嵌入式系统硬件基础知识

提示:“软考通关秘籍” 专栏围绕软考展开,全面涵盖了如嵌入式系统设计师、数据库系统工程师、信息系统管理工程师等多个软考方向的知识点。从计算机体系结构、存储系统等基础知识,到程序语言概述、算法、数据库技术(包括关系数据库、非关系型数据库、SQL 语言、数据仓库等)…

Vue2_Vue.js教程

目录 一、Vue.js安装 1、独立版本 2、CDN 方法 3、npm 方法 二、Vue Al编程助手 三、Vue.js目录结构 目录解析 四、Vue.js 起步 1.如何定义数据对象和方法并渲染进页面 五、Vue.js 模板语法 插值 文本_{{}} Html_v-html 指令 属性_v-bind (数据传输工具)指令 表…

【工业场景】用YOLOv12实现饮料类别识别

饮料类别识别任务的意义在于帮助人们更快速地识别和区分不同类型的饮料&#xff0c;从而提高消费者的购物体验和满意度。对于商家而言&#xff0c;饮料类别识别可以帮助他们更好地管理库存、优化货架布局和预测销售趋势&#xff0c;从而提高运营效率和利润。此外&#xff0c;饮…

从小米汽车事故反思 LabVIEW 开发

近期&#xff0c;小米汽车的一起严重事故引发了社会各界的广泛关注。这起事故不仅让我们对智能汽车的安全性产生了深深的思考&#xff0c;也为 LabVIEW 开发领域带来了诸多值得汲取的知识与领悟。 在智能汽车领域&#xff0c;尤其是涉及到智能驾驶辅助系统时&#xff0c;安全是…

Vue3+Vite+TypeScript+Element Plus开发-04.静态菜单设计

系列文档目录 Vue3ViteTypeScript安装 Element Plus安装与配置 主页设计与router配置 静态菜单设计 Pinia引入 文章目录 目录 系列文档目录 文章目录 前言 一、Aside设计 二、动态增加菜单 三.布局引用在Main中显示 参考文献&#xff1a; 前言 在本系列文档中&…

大数据技术发展与应用趋势分析

大数据技术发展与应用趋势分析 文章目录 大数据技术发展与应用趋势分析1. 大数据概述2 大数据技术架构2.1 数据采集层2.2 数据存储层2.3 数据处理层2.4 数据分析层 3 大数据发展趋势3.1 AI驱动的分析与自动化3.2 隐私保护分析技术3.3 混合云架构的普及3.4 数据网格架构3.5 量子…

与Linux操作系统相关的引导和服务

目录 一.Linux操作系统引导过程 1.1引导过程总览 1.2系统初始化进程 1.2.1init进程 1.2.2sysmted 1.3systemd单元类型 二.排除启动类故障 2.1MBR扇区故障 2.1.1故障原因 2.1.2故障现象 2.1.3解决办法 2.1.4模拟修复MBR扇区故障 1)添加新的硬盘 2&#xff09;进行…

STM32单片机入门学习——第16节: [6-4] PWM驱动LED呼吸灯PWM驱动舵机PWM驱动直流电机

写这个文章是用来学习的,记录一下我的学习过程。希望我能一直坚持下去,我只是一个小白,只是想好好学习,我知道这会很难&#xff0c;但我还是想去做&#xff01; 本文写于&#xff1a;2025.04.05 STM32开发板学习——第16节: [6-4] PWM驱动LED呼吸灯&PWM驱动舵机&PWM驱…

基础框架系列分享:一个通用的Excel报表生成管理框架

由于我们系统经常要生成大量的Excel报表&#xff08;Word&#xff0c;PDF报表也有&#xff0c;另行分享&#xff09;&#xff0c;最初始他们的方案是&#xff0c;设计一个表&#xff0c;和Excel完全对应&#xff0c;然后读表&#xff0c;把数据填进去&#xff0c;这显然是非常不…

Ansible(4)—— Playbook

目录 一、Ansible Playbook : 1、Play &#xff1a; 2、Playbook&#xff1a; 二、Ansible Playbook 格式&#xff1a; 1、空格&#xff1a; 2、破折号&#xff08; - &#xff09;&#xff1a; 3、Play 格式&#xff1a; 三、查找用于任务的模块&#xff1a; 1、模块…

自学-C语言-基础-数组、函数、指针、结构体和共同体、文件

这里写自定义目录标题 代码环境&#xff1a;&#xff1f;问题思考&#xff1a;一、数组二、函数三、指针四、结构体和共同体五、文件问题答案&#xff1a; 代码环境&#xff1a; Dev C &#xff1f;问题思考&#xff1a; 把上门的字母与下面相同的字母相连&#xff0c;线不能…

蓝桥云客--团队赛

2.团队赛【算法赛】 - 蓝桥云课 问题描述 蓝桥杯最近推出了一项团队赛模式&#xff0c;要求三人组队参赛&#xff0c;并规定其中一人必须担任队长。队长的资格很简单&#xff1a;其程序设计能力值必须严格大于其他两名队友程序设计能力值的总和。 小蓝、小桥和小杯正在考虑报名…

C-S模式之实现一对一聊天

天天开心&#xff01;&#xff01;&#xff01; 文章目录 一、如何实现一对一聊天&#xff1f;1. 服务器设计2. 客户端设计3. 服务端代码实现4. 客户端代码实现5. 实现说明6.实验结果 二、改进常见的服务器高并发方案1. 多线程/多进程模型2. I/O多路复用3. 异步I/O&#xff08;…

[Deep-ML]Transpose of a Matrix(矩阵的转置)

Transpose of a Matrix&#xff08;矩阵的转置&#xff09; 题目链接&#xff1a; Transpose of a Matrix&#xff08;矩阵的转置&#xff09;https://www.deep-ml.com/problems/2 题目描述&#xff1a; 难度&#xff1a; easy&#xff08;简单&#xff09;。 分类&#…

智慧节能双突破 强力巨彩谷亚VK系列刷新LED屏使用体验

当前全球节能减排趋势明显&#xff0c;LED节能屏作为显示技术的佼佼者&#xff0c;正逐渐成为市场的新宠。强力巨彩谷亚万境VK系列节能智慧屏凭借三重技术保障、四大智能设计以及大师臻彩画质&#xff0c;在实现节能效果的同时&#xff0c;更在智慧显示领域树立新的标杆。   …

html 给文本两端加虚线自适应

效果图&#xff1a; <div class"separator">文本 </div>.separator {width: 40%;border-style: dashed;display: flex;align-items: center;color: #e2e2e2;font-size: 14px;line-height: 20px;border-color: #e2e2e2;border-width: 0; }.separator::bef…

leetcode4.寻找两个正序数组中的中位数

思路源于 LeetCode004-两个有序数组的中位数-最优算法代码讲解 基本思路是将两个数组看成一个数组&#xff0c;然后划分为两个部分&#xff0c;若为奇数左边部分个数多1&#xff0c;若为偶数左边部分等于右边部分个数。i表示数组1划分位置&#xff08;i为4是索引4也表示i的左半…