以下是前后端分離架構下,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 {@Autowiredprivate UserService userService;@GetMappingpublic 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 {@Idprivate Long id;private String name;private String email;
}
-
DAO 實質上是 Repository 接口本身(即 Data Access Object)
-
Entity 是 ORM 框架(JPA/Hibernate)使用的 Java → 表 的映射結構
-
從數據庫返回的是 Entity 實例,非 DTO,非 DAO
九、響應流程(步驟12-17)
數據返回結構:
步驟 | 返回對象 | 類型 |
---|---|---|
12 | Repository → Service | Entity 實體類 |
13 | Service → Controller | DTO(如 UserDto) |
14 | Controller → HandlerAdapter | ResponseEntity 或直接 DTO |
15 | HandlerAdapter → DispatcherServlet | 包裝為 ModelAndView(內部結構) |
17 | DispatcherServlet → 前端 | 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)
? 先明確三個概念:
注解 | 用于 | 描述 |
---|---|---|
| 控制器或異常處理類中的方法 | 聲明某個方法用來處理特定類型異常 |
| 類級別 | 用于集中定義所有控制器的全局異常處理 |
|
| 自動將返回值作為JSON響應 |
? 情況分類:異常處理的三個層級
異常發生層 | 誰處理 | 示例 |
---|---|---|
Controller層內部異常 | 當前 Controller 類中的 | 請求參數缺失、非法訪問 |
Service層拋出業務異常 | 交由 Controller 接收并傳播給 | 用戶名重復、余額不足 |
Repository層拋出數據訪問異常 | Service 可以選擇 catch/轉義,也可以上拋 | JPA拋出 |
?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 結構 |
ModelAndView | Spring MVC 內部統一封裝結構 |
JWT | JSON Web Token,認證令牌,由后端生成 |
Authentication | Spring Security 中用于表示認證用戶的對象 |
SecurityContextHolder | 保存當前請求上下文的認證信息 |
@PreAuthorize | 方法級授權注解,執行方法前做權限判斷 |