JavaEE进阶——SpringBoot统一特性处理实战指南
目录
8. 进阶:为什么 Spring MVC 需要适配器模式?(选读)
Spring Boot统一功能处理详解(新手完整版)
前置准备:本教程使用 Lombok 简化代码(如
@Data,@Slf4j),请务必在pom.xml中添加依赖,并在 IDE 中安装 Lombok 插件,否则代码会报错。org.projectlombok lombok true
1. 拦截器详解
1.1 什么是拦截器
拦截器是Spring MVC提供的"安检门"机制,能在请求到达Controller之前、之后以及请求完成时插入自定义逻辑。它就像你去商场时要经过的安检:安检前检查包裹(preHandle),安检后刷卡(postHandle),离开时记录时间(afterCompletion)。
核心应用场景:
登录认证:检查用户是否登录(如未登录不能访问订单页面)
日志记录:记录每个请求的处理时间
权限控制:判断用户是否有权限访问某个接口
性能监控:统计接口响应时间
1.2 完整代码实现(逐行注释)
1.2.1 定义登录拦截器(传统Session方式)
⚠️ 注意:Session方式适合传统Web页面项目。对于现代前后端分离项目(Vue/React + Spring Boot),推荐使用 JWT Token 方案(见1.2.3节)。
// import关键字:导入其他包中的类,就像你要用别人的工具得先拿来
// slf4j:Simple Logging Facade for Java,日志门面框架,类似一个日志的"翻译官"
// 它能让你在不改代码的情况下切换log4j、logback等具体实现
import lombok.extern.slf4j.Slf4j;
// Spring框架的组件注解,标记这个类为Spring管理的Bean(就像商品贴上条形码入库)
// Spring容器会自动创建它的实例,其他地方可以直接"借用"
import org.springframework.stereotype.Component;
// Spring MVC的核心接口,实现它就拥有了拦截请求的能力
// 类似"安检员资格证",只有拿到这个证才能在指定位置检查
import org.springframework.web.servlet.HandlerInterceptor;
// Servlet规范提供的HTTP请求对象,封装了客户端发送的所有信息
// 包括请求头、参数、Cookie等,相当于"快递包裹单"
import jakarta.servlet.http.HttpServletRequest;
// Servlet规范提供的HTTP响应对象,用于向客户端返回数据
// 相当于"快递回执单",你可以填写返回内容和状态
import jakarta.servlet.http.HttpServletResponse;
// Session是会话对象,用于在多次请求间保存用户状态
// 就像商场的储物柜,存一次东西,多次取(前提是有钥匙)
import jakarta.servlet.http.HttpSession;
/**
* 登录拦截器(Session版)
* @Slf4j:Lombok注解,自动生成日志记录器
* @Component:让Spring管理这个拦截器
*/
@Slf4j
@Component
public class LoginInterceptor implements HandlerInterceptor {
/**
* preHandle:在Controller方法执行前调用(安检门第一道关卡)
* 返回true = 放行(绿灯),返回false = 拦截(红灯)
* * @param request HTTP请求对象(包裹单)
* @param response HTTP响应对象(回执单)
* @param handler 要执行的Controller方法(目标商店)
* @return boolean 是否允许通过
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
log.info("LoginInterceptor.preHandle() - 开始检查用户登录状态, URI: {}", request.getRequestURI());
// 获取Session,参数false表示"没有就别新建"
HttpSession session = request.getSession(false);
// 检查Session是否存在且包含用户信息
// &&是短路与,左边为false右边不执行(避免空指针)
if (session != null && session.getAttribute("user") != null) {
log.info("用户已登录,放行请求");
return true; // 放行
}
// 没登录,设置401状态码(Unauthorized,未授权)
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); // SC_UNAUTHORIZED就是401的常量
log.warn("用户未登录,请求被拦截");
return false; // 拦截,不执行后续操作
}
// ... postHandle和afterCompletion方法同上,省略 ...
}
1.2.3 定义登录拦截器(现代Token方式,推荐)
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
/**
* 登录拦截器(JWT Token版 - 前后端分离项目推荐)
* 从请求头中获取Token进行验证
*/
@Slf4j
@Component
public class TokenInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
log.info("TokenInterceptor.preHandle() - 开始验证Token, URI: {}", request.getRequestURI());
// 从请求头中获取Token(前端在Header中传递:Authorization: Bearer xxxx)
String token = request.getHeader("Authorization");
// 简单的Token验证逻辑(实际应调用JWT工具类解析)
if (token != null && token.startsWith("Bearer ") && validateToken(token)) {
log.info("Token验证通过,放行请求");
return true;
}
// Token无效或过期
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
log.warn("Token验证失败,请求被拦截");
return false;
}
/**
* 模拟Token验证方法(实际应使用JWT库解析)
* @param token 请求头中的Token字符串
* @return 是否有效
*/
private boolean validateToken(String token) {
// 实际业务:解析JWT,验证签名是否有效、是否过期
// 例如:return JwtUtil.verify(token.replace("Bearer ", ""));
return "Bearer valid-token".equals(token); // 模拟实现
}
}
1.3 注册拦截器到Spring MVC
// ... import 语句(略) ...
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Autowired
private LoginInterceptor loginInterceptor; // 或 TokenInterceptor
// 定义不需要拦截的路径列表
private List excludePaths = Arrays.asList(
"/user/login", // 登录接口
"/user/register", // 注册接口
"/api/auth/refresh", // Token刷新接口(Token方式需要)
"/static/**", // 静态资源
"/error/**" // 错误页面
);
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loginInterceptor) // 或 tokenInterceptor
.addPathPatterns("/**") // 先全部拦截
.excludePathPatterns(excludePaths); // 再开放白名单
log.info("拦截器注册完成,已应用登录验证");
}
}
2. 统一数据返回格式
2.1 为什么需要统一格式?
想象你是前端开发,后端同事张三返回{"name":"张三"},李四返回"李四",王五返回true。你要写三种不同的解析逻辑,维护成本极高!
统一格式后,所有接口都返回:
{
"status": 200,
"data": "实际数据",
"errorMessage": "",
"timestamp": 1234567890
}
前端只需写一套解析逻辑:取data字段即可。
2.2 完整实现代码
2.2.1 统一结果类Result
import lombok.Data;
/**
* 统一返回结果类
* @param 泛型,表示data字段可以是任何类型
*/
@Data
public class Result {
private int status;
private String errorMessage;
private T data;
private long timestamp;
private Result() {
this.timestamp = System.currentTimeMillis();
}
public static Result success(T data) {
Result result = new Result<>();
result.setStatus(200);
result.setData(data);
return result;
}
public static Result fail(String errorMessage) {
Result result = new Result<>();
result.setStatus(500);
result.setErrorMessage(errorMessage);
return result;
}
public static Result custom(int status, String errorMessage, T data) {
Result result = new Result<>();
result.setStatus(status);
result.setErrorMessage(errorMessage);
result.setData(data);
return result;
}
}
2.2.2 全局响应处理器ResponseAdvice
// ... import 语句(略) ...
@Slf4j
@ControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice

浙公网安备 33010602011771号