springboot系列--拦截器执行原理

一、拦截器核心概念

一、定义

拦截器(Interceptor)是框架级别的组件,用于在请求的不同阶段(如到达控制器之前(也就是接口)、处理完成之后)动态地拦截和处理 HTTP 请求。

二、使用场景

一、用户认证和授权

复制代码
if (!isValidToken(request.getHeader("Authorization"))) {
    response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
    response.getWriter().write("Unauthorized");
    return false;
}

二、请求日志记录

复制代码
long startTime = System.currentTimeMillis();
request.setAttribute("startTime", startTime);

@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
    long endTime = System.currentTimeMillis();
    long executionTime = endTime - (long) request.getAttribute("startTime");
    System.out.println("URI: " + request.getRequestURI() + " | 耗时: " + executionTime + " ms");
}

三、性能监控

统计每个请求的执行时间并记录慢请求日志。

四、跨域处理

设置跨域响应头(更推荐用 CorsRegistry 实现)。

三、执行流程

  1. preHandle:请求到达控制器之前执行。
  2. 控制器逻辑(也就是接口):拦截器放行后,执行目标控制器的方法。
  3. postHandle:控制器逻辑处理完成后(视图渲染前)执行。
  4. afterCompletion:视图渲染完成后执行(或者每个拦截器执行出现异常后执行),用于资源清理或日志输出。

二、拦截器的实现与配置

一、创建拦截器

拦截器通过实现 HandlerInterceptor 接口来定义。该接口包含以下三个核心方法:

  • preHandle

    在目标方法调用之前执行,用于权限验证或日志记录。

    返回 true 表示继续处理,返回 false 表示中断请求。

  • postHandle

    在目标方法执行之后,视图渲染之前执行。

    适合处理返回数据或修改模型数据。

  • afterCompletion

    在视图渲染完成后执行,用于资源清理或捕获异常。

复制代码
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class LoggingInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("preHandle: 开始拦截请求");
        // 检查用户是否登录
        String token = request.getHeader("Authorization");
        if (token == null || !token.startsWith("Bearer ")) {
            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            return false; // 拦截请求
        }
        return true; // 放行
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("postHandle: 请求处理完成");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("afterCompletion: 视图渲染完成或拦截器出现异常");
    }
}

二、注册拦截器

拦截器需要在配置类中注册,并指定拦截路径和排除路径。

复制代码
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 LoggingInterceptor())
                .addPathPatterns("/**") // 拦截所有请求
                .excludePathPatterns("/login", "/error", "/static/**"); // 排除的路径
    }
}

三、拦截器与过滤器的区别

四 、拦截器的高级用法

一、链式拦截器

可以注册多个拦截器,Spring 会按注册顺序依次执行。

preHandle 方法中,任一拦截器返回 false 都会中断后续处理。

复制代码
registry.addInterceptor(new AuthInterceptor()).addPathPatterns("/**");
registry.addInterceptor(new LoggingInterceptor()).addPathPatterns("/**");

执行顺序:

  • preHandleAuthInterceptor -> LoggingInterceptor
  • postHandleLoggingInterceptor -> AuthInterceptor
  • afterCompletionLoggingInterceptor -> AuthInterceptor

二、拦截器与异步请求

对于异步请求(如使用 @Async),postHandle 可能不会被触发。如果需要确保拦截器对异步请求生效,可以使用 AsyncHandlerInterceptor 接口。

复制代码
import org.springframework.web.servlet.AsyncHandlerInterceptor;

public class AsyncLoggingInterceptor implements AsyncHandlerInterceptor {
    // 同步和异步请求都可拦截
}

三、动态拦截路径

拦截器的路径规则可以通过配置文件或数据库动态加载。

复制代码
@Value("${interceptor.paths}")
private List<String> paths;

@Override
public void addInterceptors(InterceptorRegistry registry) {
    registry.addInterceptor(new DynamicPathInterceptor())
            .addPathPatterns(paths.toArray(new String[0]));
}

interceptor:
  paths:
    - /api/**
    - /admin/**

五、常见问题

一、preHandle 拦截中断后,如何返回自定义响应?

可以直接通过 HttpServletResponse 写入 JSON 或其他格式的响应:

response.setContentType("application/json;charset=UTF-8");

response.getWriter().write("{\"error\":\"Unauthorized\"}");

return false;

二、拦截器的执行顺序与性能优化

  • 尽量减少复杂计算或阻塞操作。
  • 使用 excludePathPatterns 排除无需拦截的静态资源或公共接口。
  • 结合 AOP 实现增强功能

六、拦截器底层原

请求一进来都是通过前端控制器DispatcherServlet进行处理,然后处理器映射器找到具体的handler,在通过处理器适配器,适配到具体的handler中进行处理。

复制代码
	// 直接进入到DispatcherServlet这个类里面的这个方法
	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 {
				processedRequest = checkMultipart(request);
				multipartRequestParsed = (processedRequest != request);

				// Determine handler for the current request. 这里就是获取具体的handler,也就是知道了具体的controller中的那个方法可以处理
				mappedHandler = getHandler(processedRequest);
				if (mappedHandler == null) {
					noHandlerFound(processedRequest, response);
					return;
				}

				// Determine handler adapter for the current request. 这里就是获取处理器适配器
				HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

				// Process last-modified header, if supported by the handler.
				String method = request.getMethod();
				boolean isGet = "GET".equals(method);
				if (isGet || "HEAD".equals(method)) {
					long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
					if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
						return;
					}
				}
				// 这里就是处理拦截器的preHandle方法。如果返回为false,这里取反,就进入,然后直接退出。
				if (!mappedHandler.applyPreHandle(processedRequest, response)) {
					return;
				}

				// Actually invoke the handler. 这里就是之前研究过的去执行具体的handler,也就是具体的接口,nv就是执行完接口后的返回数据封装的ModelAndView对象
				mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

				if (asyncManager.isConcurrentHandlingStarted()) {
					return;
				}

				applyDefaultViewName(processedRequest, mv);
				// 这里就是开始执行拦截器的postHandle方法
				mappedHandler.applyPostHandle(processedRequest, response, mv);
			}
			catch (Exception ex) {
				dispatchException = ex;
			}
			catch (Throwable err) {
				// As of 4.3, we're processing Errors thrown from handler methods as well,
				// making them available for @ExceptionHandler methods and other scenarios.
				dispatchException = new NestedServletException("Handler dispatch failed", err);
			}
			// 这里就开始进行视图解析等各种操作
			processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
		}
		catch (Exception ex) {
		// 以上操作如果出现异常,都会执行拦截器的afterCompletion发明合法
			triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
		}
		catch (Throwable err) {
		// 以上操作如果出现异常,都会执行拦截器的afterCompletion发明合法
			triggerAfterCompletion(processedRequest, response, mappedHandler,
					new NestedServletException("Handler processing failed", err));
		}
		finally {
			if (asyncManager.isConcurrentHandlingStarted()) {
				// Instead of postHandle and afterCompletion
				if (mappedHandler != null) {
					mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
				}
			}
			else {
				// Clean up any resources used by a multipart request.
				if (multipartRequestParsed) {
					cleanupMultipart(processedRequest);
				}
			}
		}
	}
	
	
	
	
	
	
		// HandlerExecutionChain类下的方法,这里会循环执行拦截器链中的preHandle方法,注意,这里是顺序执行,也就是从第一个开始执行
		boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
		for (int i = 0; i < this.interceptorList.size(); i++) {
			HandlerInterceptor interceptor = this.interceptorList.get(i);
			if (!interceptor.preHandle(request, response, this.handler)) {
				// 如果拦截器链中的方法preHandle,返回为false,进入这里执行拦截器中给的异常方法afterCompletion,然后返回false,中断循环,之后的拦截器都不执行了,如果为true,则执行下一个拦截器
				triggerAfterCompletion(request, response, null);
				return false;
			}
			this.interceptorIndex = i;
		}
		return true;
	}
	
	
	
	
	// HandlerExecutionChain类下的方法,注意这里就是拦截器出现异常开始执行的方法,这里是倒序执行,也就是开始是先执行preHandle方法的拦截器,如果出现异常就会倒序执行
	// afterCompletion方法,也就是之前限制性preHandle方法的拦截器,变成了后执行afterCompletion方法。
	void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex) {
		for (int i = this.interceptorIndex; i >= 0; i--) {
			HandlerInterceptor interceptor = this.interceptorList.get(i);
			try {
				interceptor.afterCompletion(request, response, this.handler, ex);
			}
			catch (Throwable ex2) {
				logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
			}
		}
	}
	
	
	
	
	// HandlerExecutionChain类下的方法,这里执行完目标方法,也就是接口中的方法后,执行拦截器中的PostHandle方法,这个也是倒序执行
		void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv)
			throws Exception {

		for (int i = this.interceptorList.size() - 1; i >= 0; i--) {
			HandlerInterceptor interceptor = this.interceptorList.get(i);
			interceptor.postHandle(request, response, this.handler, mv);
		}
	}
	
	
	
	
	// HandlerExecutionChain类下的方法,无论哪里处理出了异常,都会执行拦截器的AfterCompletion方法
	void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex) {
		for (int i = this.interceptorIndex; i >= 0; i--) {
			HandlerInterceptor interceptor = this.interceptorList.get(i);
			try {
				interceptor.afterCompletion(request, response, this.handler, ex);
			}
			catch (Throwable ex2) {
				logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
			}
		}
	}





	
	
// DispatcherServlet类里面的方法,这里就开始进行视图解析等各种操作
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
			@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
			@Nullable Exception exception) throws Exception {

		boolean errorView = false;

		if (exception != null) {
			if (exception instanceof ModelAndViewDefiningException) {
				logger.debug("ModelAndViewDefiningException encountered", exception);
				mv = ((ModelAndViewDefiningException) exception).getModelAndView();
			}
			else {
				Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
				mv = processHandlerException(request, response, handler, exception);
				errorView = (mv != null);
			}
		}

		// Did the handler return a view to render?
		if (mv != null && !mv.wasCleared()) {
			render(mv, request, response);
			if (errorView) {
				WebUtils.clearErrorRequestAttributes(request);
			}
		}
		else {
			if (logger.isTraceEnabled()) {
				logger.trace("No view rendering, null ModelAndView returned.");
			}
		}

		if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
			// Concurrent handling started during a forward
			return;
		}
	
		// 页面渲染完也会执行拦截器的AfterCompletion方法
		if (mappedHandler != null) {
			// Exception (if any) is already handled..
			mappedHandler.triggerAfterCompletion(request, response, null);
		}
	}

总结:

1、根据当前请求,找到HandlerExecutionChain【可以处理请求的handler以及handler的所有 拦截器】

2、先来顺序执行 所有拦截器的 preHandle方法

  • 1、如果当前拦截器prehandler返回为true。则执行下一个拦截器的preHandle
  • 2、如果当前拦截器返回为false。直接 倒序执行所有已经执行了的拦截器的 afterCompletion;

3、如果任何一个拦截器返回false。直接跳出不执行目标方法

4、所有拦截器都返回True。执行目标方法

5、倒序执行所有拦截器的postHandle方法。

6、前面的步骤有任何异常都会直接倒序触发 afterCompletion

7、页面成功渲染完成以后,也会倒序触发 afterCompletion

相关推荐
开开心心就好4 分钟前
高效全能PDF工具,支持OCR识别
java·前端·python·pdf·ocr·maven·jetty
冷心笑看丽美人5 分钟前
Spring MVC数据绑定和响应 你了解多少?
java·spring·mvc
XQ丶YTY12 分钟前
大二java第一面小厂(挂)
java·开发语言·笔记·学习·面试
呆萌很24 分钟前
基于 Spring Boot 瑞吉外卖系统开发(十四)
spring boot
一零贰肆28 分钟前
深入理解SpringBoot中的SpringCache缓存技术
java·springboot·springcache·缓存技术
码上飞扬1 小时前
Java大师成长计划之第22天:Spring Cloud微服务架构
java·运维·云计算
秋野酱1 小时前
基于javaweb的SpringBoot自习室预约系统设计与实现(源码+文档+部署讲解)
java·spring boot·后端
呆萌很1 小时前
基于 Spring Boot 瑞吉外卖系统开发(十五)
spring boot
weloveut2 小时前
西门子WinCC Unified PC的GraphQL使用手册
后端·python·graphql
面试官E先生2 小时前
【极兔快递Java社招】一面复盘|数据库+线程池+AQS+中间件面面俱到
java·面试