完整教程:JavaEE进阶——SpringBoot拦截器详解:从入门到实战
2026-01-29 08:06 tlnshuju 阅读(0) 评论(0) 收藏 举报目录
Spring Boot统一功能处理详解
1. 拦截器详解
1.1 什么是拦截器
拦截器(Interceptor)是Spring框架提供的一种机制,用于在请求处理的不同阶段(请求前、请求后、视图渲染后)插入自定义逻辑。它类似于Web开发中的过滤器(Filter),但拦截器是基于Java反射机制实现的,工作在DispatcherServlet之后,属于Spring MVC框架的一部分。
拦截器的主要应用场景包括:
- 用户身份认证和授权
- 日志记录
- 性能监控
- 防止表单重复提交
- 处理国际化
- 统一异常处理
1.2 拦截器快速入门
1.2.1 定义拦截器
首先我们来看如何定义一个基本的拦截器。以下是一个登录拦截器的完整代码:
// 导入slf4j日志框架的注解,用于生成日志记录器
import lombok.extern.slf4j.Slf4j;
// 将此类标记为Spring组件,使其被Spring容器管理
import org.springframework.stereotype.Component;
// 导入Spring MVC的拦截器接口
import org.springframework.web.servlet.HandlerInterceptor;
// 导入Servlet API中的请求、响应和会话对象
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
/**
* 登录拦截器
* 实现HandlerInterceptor接口,重写其方法
* @Slf4j注解会自动生成一个名为log的Logger对象,用于记录日志
*/
@Slf4j
@Component // 将此类注册为Spring Bean,使其可以被自动注入
public class LoginInterceptor implements HandlerInterceptor {
/**
* preHandle方法:在Controller方法执行前调用
* 返回true表示放行,继续执行后续操作
* 返回false表示拦截,中断请求处理
*
* @param request HTTP请求对象
* @param response HTTP响应对象
* @param handler 当前请求的处理器(Controller方法)
* @return boolean 是否继续处理请求
* @throws Exception 可能抛出的异常
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 记录日志,表示拦截器在目标方法执行前被触发
log.info("LoginInterceptor目标方法执行前执行..");
// 返回true,表示放行请求,继续执行Controller中的方法
return true;
}
/**
* postHandle方法:在Controller方法执行后,视图渲染前调用
*
* @param request HTTP请求对象
* @param response HTTP响应对象
* @param handler 当前请求的处理器
* @param modelAndView 视图和模型数据对象,可用于修改视图或添加属性
* @throws Exception 可能抛出的异常
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
// 记录日志,表示拦截器在目标方法执行后被触发
log.info("LoginInterceptor目标方法执行后执行");
}
/**
* afterCompletion方法:在整个请求完成,视图渲染完毕后调用
* 这是拦截器的最后一个方法,通常用于资源清理
*
* @param request HTTP请求对象
* @param response HTTP响应对象
* @param handler 当前请求的处理器
* @param ex 在处理过程中发生的异常,如果没有异常则为null
* @throws Exception 可能抛出的异常
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
Exception ex) throws Exception {
// 记录日志,表示拦截器在视图渲染完毕后执行
log.info("LoginInterceptor视图渲染完毕后执行,最后执行");
}
}
1.2.2 注册配置拦截器
定义了拦截器后,还需要将其注册到Spring MVC中。以下是注册配置拦截器的代码:
// 导入Spring的依赖注入注解
import org.springframework.beans.factory.annotation.Autowired;
// 标识此类为配置类,替代xml配置
import org.springframework.context.annotation.Configuration;
// 导入Web MVC配置相关的接口和类
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* Web配置类
* 实现WebMvcConfigurer接口,用于自定义Spring MVC配置
*/
@Configuration // 标记为配置类,Spring启动时会加载此类
public class WebConfig implements WebMvcConfigurer {
// 自动注入之前定义的LoginInterceptor拦截器
@Autowired
private LoginInterceptor loginInterceptor;
/**
* 添加拦截器到注册表
* 该方法会被Spring MVC自动调用
*
* @param registry 拦截器注册表,用于注册和配置拦截器
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 注册自定义拦截器
// addPathPatterns设置拦截路径,"/**"表示拦截所有请求
registry.addInterceptor(loginInterceptor)
.addPathPatterns("/**");
}
}
1.2.3 拦截器执行流程
当我们启动服务并访问任意请求时,可以通过日志观察到拦截器的执行顺序:
- 首先执行
preHandle()方法 - 然后执行Controller中的目标方法
- 接着执行
postHandle()方法 - 最后执行
afterCompletion()方法
如果preHandle()方法返回false,则后续的Controller方法和拦截器的其他方法都不会被执行,请求被拦截。
1.3 拦截器详解
1.3.1 拦截路径配置
在实际应用中,我们通常需要精确控制哪些路径需要拦截,哪些路径不需要拦截。例如,登录页面本身就不需要进行登录验证。以下是更完善的拦截器配置:
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;
import java.util.Arrays;
import java.util.List;
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Autowired
private LoginInterceptor loginInterceptor;
// 定义不需要拦截的路径集合
private List excludePaths = Arrays.asList(
"/user/login", // 登录接口
"/**/*.js", // 所有JS静态资源
"/**/*.css", // 所有CSS静态资源
"/**/*.png", // 所有PNG图片
"/**/*.html" // 所有HTML页面
);
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loginInterceptor)
.addPathPatterns("/**") // 拦截所有请求
.excludePathPatterns(excludePaths); // 排除指定的路径
}
}
常见的拦截路径配置模式:
| 拦截路径 | 含义 | 举例 |
|---|---|---|
/* | 一级路径 | 能匹配/user,/book,/login,不能匹配/user/login |
/** | 任意级路径 | 能匹配/user,/user/login,/user/reg |
/book/* | /book下的一级路径 | 能匹配/book/addBook,不能匹配/book/addBook/1,/book |
/book/** | /book下的任意级路径 | 能匹配/book,/book/addBook,/book/addBook/2,不能匹配/user/login |
注意:这些拦截规则同样适用于静态文件(如图片、JS、CSS等)。
1.3.2 登录校验拦截器实现
下面是一个实际的登录校验拦截器实现,它会检查Session中是否存在用户信息:
// 导入项目常量
import com.example.demo.constant.Constants;
// 日志注解
import lombok.extern.slf4j.Slf4j;
// Spring组件注解
import org.springframework.stereotype.Component;
// 拦截器接口
import org.springframework.web.servlet.HandlerInterceptor;
// Servlet API
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
/**
* 登录校验拦截器
*/
@Slf4j
@Component
public class LoginInterceptor implements HandlerInterceptor {
/**
* 在Controller方法执行前进行登录校验
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 获取Session对象,参数false表示如果Session不存在不创建新的Session
HttpSession session = request.getSession(false);
// 检查Session是否存在且包含用户信息
if (session != null && session.getAttribute(Constants.SESSION_USER_KEY) != null) {
// Session中有用户信息,放行请求
return true;
}
// 未登录,设置HTTP状态码为401(未授权)
response.setStatus(401);
// 拦截请求
return false;
}
}
HTTP状态码401详解: 401状态码表示"Unauthorized",即未经过认证。它指示身份验证是必需的,且没有提供身份验证凭证或身份验证失败。如果请求已经包含授权凭据,那么401状态码表示服务器不接受这些凭据。
在实际应用中,前端可以根据这个状态码跳转到登录页面,提示用户进行登录。
1.4 DispatcherServlet源码分析
1.4.1 什么是DispatcherServlet
DispatcherServlet是Spring MVC的核心,它是一个前端控制器(Front Controller),负责接收所有HTTP请求,并将请求分派给适当的处理器(Controller)。它还负责请求处理的整个生命周期,包括:
- 初始化Web应用上下文
- 解析请求
- 处理多部分请求(文件上传)
- 查找处理器
- 应用拦截器
- 处理异常
- 渲染视图
1.4.2 初始化过程
当Tomcat启动后,DispatcherServlet会执行初始化方法。以下是简化版的初始化流程:
@Override
public final void init() throws ServletException {
try {
// 1. 加载配置参数
PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
// 2. 构造DispatcherServlet
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
initBeanWrapper(bw);
bw.setPropertyValues(pvs, true);
} catch (BeansException ex) {
// 异常处理
}
// 3. 调用子类实现的初始化方法
initServletBean();
}
initServletBean()方法在FrameworkServlet类中实现,主要负责创建Web应用上下文(ApplicationContext)。
1.4.3 处理请求流程
当请求到达DispatcherServlet时,会调用doDispatch()方法处理请求:
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
// 1. 处理文件上传
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// 2. 获取处理器执行链(包括处理器和拦截器)
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
// 3. 获取处理器适配器
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// 4. 执行拦截器的preHandle方法
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// 5. 执行目标方法(Controller方法)
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
// 6. 应用默认视图名称(如果需要)
applyDefaultViewName(processedRequest, mv);
// 7. 执行拦截器的postHandle方法
mappedHandler.applyPostHandle(processedRequest, response, mv);
} catch (Exception ex) {
dispatchException = ex;
} catch (Throwable err) {
dispatchException = new NestedServletException("Handler dispatch failed", err);
}
// 8. 处理分发结果(包括渲染视图)
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
} catch (Exception ex) {
// 9. 触发完成处理(包括执行拦截器的afterCompletion方法)
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
} finally {
// 10. 清理资源
if (asyncManager.isConcurrentHandlingStarted()) {
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
} else {
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}
拦截器执行流程详解: 在doDispatch()方法中,拦截器的执行主要分为三个阶段:
applyPreHandle():在Controller方法执行前调用所有拦截器的preHandle()方法applyPostHandle():在Controller方法执行后、视图渲染前调用所有拦截器的postHandle()方法triggerAfterCompletion():在视图渲染完成后调用所有拦截器的afterCompletion()方法
以下是applyPreHandle()方法的实现:
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
// 遍历所有拦截器
for (int i = 0; i < this.interceptorList.size(); i++) {
HandlerInterceptor interceptor = this.interceptorList.get(i);
// 调用拦截器的preHandle方法
if (!interceptor.preHandle(request, response, this.handler)) {
// 如果返回false,触发已完成处理(执行之前已经执行过的拦截器的afterCompletion方法)
triggerAfterCompletion(request, response, null);
return false;
}
this.interceptorIndex = i; // 记录已经执行的拦截器索引
}
return true; // 所有拦截器都放行
}
1.4.4 适配器模式在Spring MVC中的应用
HandlerAdapter是Spring MVC中适配器模式的典型应用。适配器模式将一个类的接口转换成客户端期望的另一个接口,使得原本不兼容的类可以一起工作。
适配器模式的角色:
- Target(目标接口):客户端期望的接口
- Adaptee(适配者):需要被适配的类
- Adapter(适配器):将Adaptee适配到Target的类
- Client(客户端):使用目标接口的对象
在Spring MVC中,HandlerAdapter就是适配器,它将各种不同类型的处理器(Controller)适配到统一的请求处理流程中。
适配器模式示例: 假设我们有不同类型的控制器:
// 传统Controller接口
public interface Controller {
ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception;
}
// 基于注解的控制器
@Controller
public class MyController {
@RequestMapping("/hello")
public String hello() {
return "hello";
}
}
// HttpRequestHandler类型
public class MyHttpRequestHandler implements HttpRequestHandler {
@Override
public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.getWriter().write("Hello from HttpRequestHandler");
}
}
Spring MVC使用不同的HandlerAdapter来适配这些不同的控制器:
// 适配Controller接口
public class SimpleControllerHandlerAdapter implements HandlerAdapter {
@Override
public boolean supports(Object handler) {
return handler instanceof Controller;
}
@Override
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return ((Controller) handler).handleRequest(request, response);
}
}
// 适配注解控制器
public class RequestMappingHandlerAdapter implements HandlerAdapter {
// 复杂的实现,处理@RequestMapping等注解
}
// 适配HttpRequestHandler
public class HttpRequestHandlerAdapter implements HandlerAdapter {
@Override
public boolean supports(Object handler) {
return handler instanceof HttpRequestHandler;
}
@Override
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
((HttpRequestHandler) handler).handleRequest(request, response);
return null;
}
}
通过适配器模式,DispatcherServlet可以统一对待所有类型的控制器,而不必关心它们的具体实现。
2. 统一数据返回格式
2.1 为什么需要统一数据返回格式
在Web开发中,前后端分离架构已成为主流。统一的数据返回格式有以下优点:
- 方便前端统一处理响应数据
- 降低前后端沟通成本
- 便于维护和扩展
- 统一错误处理机制
- 便于API文档生成和测试
通常,一个标准的响应格式包含以下字段:
- code/status:状态码,表示请求结果
- message:描述信息,提供更详细的说明
- data:实际业务数据
- timestamp:时间戳,表示响应时间
2.2 快速入门
Spring Boot提供了@ControllerAdvice和ResponseBodyAdvice来实现全局统一数据返回格式。
// 导入Spring Web相关类
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;
// 导入自定义的Result类
import com.example.demo.model.Result;
/**
* 全局响应处理
* @ControllerAdvice注解表示这是一个控制器通知类,可以处理所有Controller的异常和返回值
*/
@ControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice
浙公网安备 33010602011771号