Bootstrap

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

 

;