


专栏:JavaEE 进阶跃迁营
个人主页:手握风云
目录
[1.1. 拦截器入门](#1.1. 拦截器入门)
[1. 拦截器核心定义](#1. 拦截器核心定义)
[2. 定义拦截器](#2. 定义拦截器)
[3. 注册配置拦截器](#3. 注册配置拦截器)
[1.2. 拦截器详解](#1.2. 拦截器详解)
[1. 拦截路径](#1. 拦截路径)
[2. 拦截器执行流程](#2. 拦截器执行流程)
[1.3. 登录校验](#1.3. 登录校验)
[2.1. 快速入门](#2.1. 快速入门)
[3.1. 核心注解](#3.1. 核心注解)
[3.2. 基础实现方式](#3.2. 基础实现方式)
[3.3. 异常匹配顺序](#3.3. 异常匹配顺序)
[3.4. 核心效果](#3.4. 核心效果)
一、拦截器
拦截器是 Spring 框架提供的核心功能之一,核心用于统一拦截用户的请求 ,可在目标方法执行前后执行预先设定的通用代码,也能在请求执行前阻止其继续运行,常用来实现登录校验、日志记录等通用操作,是解决接口通用功能处理的高效方式,其入门核心围绕定义拦截器 和注册配置拦截器两步展开。
1.1. 拦截器入门
1. 拦截器核心定义
拦截器允许开发人员在应用程序中对请求做通用性处理,在用户请求响应的前后插入自定义逻辑,也可根据业务需求拦截请求(如判断 Session 中是否有登录信息,无则拦截请求,有则放行)。就如同学校的保安,他需要判断哪些人能进,哪些人不能进。

拦截器的实现需要完成自定义拦截器 和注册配置拦截器两个核心步骤,缺一不可,且需通过指定注解让 Spring 容器管理相关类。自定义拦截器相当于告知保安的工作,注册配置拦截器相当于将保安放到指定的岗位上。
2. 定义拦截器
java
package org.springframework.web.servlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.jspecify.annotations.Nullable;
public interface HandlerInterceptor {
default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return true;
}
default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
}
default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
}
}
自定义类实现 Spring 提供的 HandlerInterceptor 接口,并重写里面的方法。preHandle 方法在请求到达具体的控制器方法之前执行,它是拦截器中最先被调用的方法。该方法返回一个布尔值,如果返回 true,表示请求继续向下传递,进入后续的拦截器或控制器;如果返回 false,则表示请求被终止。postHandle 方法在控制器方法执行完毕之后,但在视图渲染之前执行。这意味着在这个阶段,开发者已经可以访问到控制器处理后的 ModelAndView 对象,因此常用于对模型数据进行统一的修改或补充,或者根据不同的视图需求进行相应的处理。afterCompletion 方法在整个请求处理完成之后执行,即在视图渲染结束后进行回调。无论请求是否成功,只要 preHandle 返回了 true,该方法最终都会被执行。
3. 注册配置拦截器
java
package com.yang.test2_22_1.config;
import com.yang.test2_22_1.interceptor.LoginInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoginInterceptor()).addPathPatterns("/**");
}
}
1.2. 拦截器详解
1. 拦截路径
拦截路径用于指定自定义拦截器对哪些请求生效,在注册配置拦截器时通过两个核心方法配置:addPathPatterns(),指定需要拦截的请求路径;excludePathPatterns(),指定需要排除、不进行拦截的请求路径。
拦截路径支持通配符匹配,核心规则如下表,规则可拦截项目中所有 URL(包括图片、JS、CSS 等静态文件):
| 拦截路径 | 含义 |
|---|---|
/* |
一级路径 |
/** |
任意级路径 |
/api/* |
/api下的一级路径 |
/api/** |
/api下的任意级路径 |
java
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Autowired
private LoginInterceptor loginInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loginInterceptor)
.addPathPatterns("/**") // 拦截所有请求
.excludePathPatterns("/api/login"); // 排除登录请求,不拦截
}
}
2. 拦截器执行流程
正常的调用顺序:

有了拦截器之后的调用顺序:

拦截器核心作用是在Controller层方法前后插入自定义逻辑,其执行流程依赖HandlerInterceptor 接口三个核心方法,与SpringMVC请求流转紧密相关。无拦截器时,请求按"用户请求→Controller→Service→Mapper→数据库"流转,处理后原路返回。添加拦截器后,请求先被拦截:到达Controller前执行preHandle(),返回true放行继续,返回false中断;若preHandle()返回true,请求正常流转,Controller目标方法执行完、视图渲染前执行postHandle()(后端少用);视图渲染完毕执行afterCompletion()用于收尾。核心方法执行顺序为preHandle()(Controller前)→目标Controller方法→postHandle()(Controller后、视图渲染前)→afterCompletion()(视图渲染后),postHandle() 和 afterCompletion() 仅在 preHandle() 返回true时执行。
1.3. 登录校验
java
package com.yang.test2_22_1.controller;
import com.yang.test2_22_1.model.User;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.Map;
@RestController
@RequestMapping("/api")
public class LoginController {
@PostMapping("/login")
public Map<String, Object> processLogin(@RequestBody User user) {
Map<String, Object> response = new HashMap<>();
String validUsername = "admin";
String validPassword = "password123";
if (validUsername.equals(user.getUsername()) && validPassword.equals(user.getPassword())) {
response.put("success", true);
response.put("message", "登录成功!欢迎回来," + user.getUsername() + "。");
} else {
response.put("success", false);
response.put("message", "用户名或密码错误,请重试。");
}
return response;
}
@GetMapping("/secret")
public String getSecretInfo() {
return "这是一条机密信息,只有登录后的用户才能看到!";
}
}
java
package com.yang.test2_22_1.interceptor;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.jspecify.annotations.Nullable;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
@Slf4j
@Component
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
Object loginUser = request.getSession().getAttribute("LOGIN_USER");
if (loginUser == null) {
response.setStatus(HttpServletResponse.SC_ACCEPTED);
response.setContentType("application/json;charset=UTF-8");
response.getWriter().write("{\"error\": \"未登录,请先登录系统\"}");
return false;
}
log.info("执行 preHandle 方法");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
log.info("执行 postHandle 方法");
}
}
java
package com.yang.test2_22_1.config;
import com.yang.test2_22_1.interceptor.LoginInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Autowired
private LoginInterceptor loginInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loginInterceptor)
.addPathPatterns("/**")
.excludePathPatterns("/api/login", "/api/secret");
}
}


二、统一数据返回格式
2.1. 快速入门
统一的数据返回格式通过 @ControllerAdvice + ResponseBodyAdvice 组合实现,核心是对所有接口的返回结果进行统一封装,简化前后端交互。
@ControllerAdvice:控制器通知类,用于全局拦截控制器的返回结果和异常,是实现统一处理的基础注解;ResponseBodyAdvice:响应体增强接口,提供对返回数据的拦截和修改能力,需重写其核心方法。@RestControllerAdvice 注解是包含了 @ControllerAdvice 和 ResponseBodyAdvice,方法返回值直接写入 HTTP 响应体 (Body),通常转换为 JSON/XML 数据。
java
import com.example.demo.model.Result;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
@ControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice {
// 判断是否执行beforeBodyWrite方法(true=执行,false=不执行)
@Override
public boolean supports(MethodParameter returnType, Class converterType) {
return true; // 对所有接口的返回结果都进行统一封装
}
// 对返回数据进行具体封装处理
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType,
MediaType selectedContentType, Class selectedConverterType,
ServerHttpRequest request, ServerHttpResponse response) {
return Result.success(body); // 调用Result工具类封装数据
}
}
supports 方法作用是控制哪些接口的返回结果需要被统一处理,并且可通过 returnType 获取当前执行的类名和方法名,实现精准匹配。
java
// 获取当前执行的控制器类
Class<?> declaringClass = returnType.getMethod().getDeclaringClass();
// 获取当前执行的方法
Method method = returnType.getMethod();
beforeBodyWrite 方法作用是对接口返回的原始数据(body)进行封装,返回统一格式的 Result 对象。依赖 Result 工具类:需提前定义 Result 泛型类,包含 status(状态)、errorMessage(错误信息)、data(业务数据)三个核心字段。
三、统一异常处理
SpringBoot 中统一异常处理核心通过 @ControllerAdvice + @ExceptionHandler 注解组合实现,实现了项目中异常的统一捕获、处理和返回格式标准化,避免零散的异常处理逻辑,降低前后端沟通和开发成本。
3.1. 核心注解
- @ControllerAdvice:标识为控制器通知类,作用于全局,可捕获所有控制器层抛出的异常;
- @ExceptionHandler:异常处理器注解,标注在方法上,指定该方法处理的异常类型;
- @ResponseBody:若需要将异常处理结果以 JSON 等数据格式返回给前端,需在类或方法上添加该注解。
3.2. 基础实现方式
- 创建全局异常处理类,添加@ControllerAdvice和@ResponseBody注解;
- 定义异常处理方法,添加@ExceptionHandler注解,方法参数为需要处理的异常类型(如Exception),返回项目统一的结果对象(如Result);
- 方法名、类名无强制要求,核心是注解的正确使用,方法内可通过异常对象获取信息并封装统一的失败返回结果。
3.3. 异常匹配顺序
当存在多个异常处理方法时,优先匹配最具体的异常类型 ,匹配规则为:当前异常类及其子类向上依次匹配。
- 底层通过ExceptionDepthComparator按异常深度排序,抛出的异常与处理方法声明的异常类型深度越小,优先级越高;
- 例如:空指针异常(NullPointerException)会优先匹配标注@ExceptionHandler(NullPointerException.class)的方法,而非处理通用Exception的方法。
java
package com.yang.test2_25_1.exception;
import com.yang.test2_25_1.common.ApiResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {
/**
* 处理用户未找到异常
*/
@ExceptionHandler(UserNotFoundException.class)
public ApiResponse<Void> handleUserNotFoundException(UserNotFoundException e) {
log.warn("参数异常:{}", e.getMessage());
return ApiResponse.error(404, e.getMessage());
}
/**
* 处理参数异常
*/
@ExceptionHandler(IllegalArgumentException.class)
public ApiResponse<Void> handleIllegalArgumentException(IllegalArgumentException e) {
log.warn("参数异常:{}", e.getMessage());
return ApiResponse.error(400, e.getMessage());
}
/**
* 处理所有异常
*/
@ExceptionHandler(Exception.class)
public ApiResponse<Void> handleException(Exception e) {
log.error("系统内部发生未知异常", e.getMessage());
return ApiResponse.error(500, "服务器内部开小差了,请稍后再试");
}
}
3.4. 核心效果
- 所有异常统一捕获,返回格式与项目统一数据返回格式保持一致(如
Result对象),方便前端统一解析; - 避免出现前端接收到杂乱的原生异常信息,减少前后端对接问题;
- 项目异常处理逻辑集中管理,便于维护和修改,统一后端技术规范。