SpringMVC拦截器与异常处理原理深度解析

目录

  1. 引言
  2. HandlerInterceptor拦截器体系
  3. 拦截器执行链完整解析
  4. 拦截器注册与配置
  5. 异常处理体系全景
  6. HandlerExceptionResolver执行链
  7. @ExceptionHandler深度解析
  8. @ControllerAdvice全局异常处理
  9. 异常处理流程时序
  10. 拦截器与异常处理协同
  11. 实战:完整登录认证方案
  12. 总结

引言

SpringMVC的拦截器(Interceptor)和异常处理(Exception Handler)是Web层的两大核心扩展机制。拦截器负责请求的前置/后置/完成处理,异常处理负责将各类异常转换为统一的响应格式。理解这两套机制的底层原理,是掌握SpringMVC高级特性的必经之路。

本文深入剖析:

  • HandlerInterceptor三方法的执行时机与数据共享
  • HandlerExecutionChain的完整执行流程
  • HandlerExceptionResolver异常处理链
  • @ExceptionHandler方法解析与优先级
  • @ControllerAdvice的扫描与注册机制
  • 拦截器与异常处理的协同工作模式

1. HandlerInterceptor拦截器体系

1.1 接口完整定义

public interface HandlerInterceptor {
    
    /**
     * 前置处理 - 返回true继续执行,返回false中断执行
     * 
     * 执行时机:DispatcherServlet.doDispatch() -> applyPreHandle()
     *           在HandlerAdapter.handle()之前执行
     * 
     * 典型用途:
     * - 权限校验
     * - 请求日志记录
     * - 性能监控起点
     */
    default boolean preHandle(
            HttpServletRequest request, 
            HttpServletResponse response, 
            Object handler) throws Exception {
        return true;
    }
    
    /**
     * 后置处理 - Controller方法执行后、视图渲染前
     * 
     * 执行时机:DispatcherServlet.doDispatch() -> applyPostHandle()
     *           在HandlerAdapter.handle()之后、View渲染之前执行
     * 
     * 典型用途:
     * - 修改ModelAndView
     * - 添加公共数据
     * - 请求日志记录终点
     * 
     * 注意:此时Controller已执行完,但视图可能还未解析
     */
    default void postHandle(
            HttpServletRequest request, 
            HttpServletResponse response, 
            Object handler, 
            @Nullable ModelAndView modelAndView) throws Exception {
    }
    
    /**
     * 完成处理 - 视图渲染完成后执行,无论是否发生异常
     * 
     * 执行时机:DispatcherServlet.render()之后,或任何阶段抛出异常后
     *           通过triggerAfterCompletion()调用
     * 
     * 典型用途:
     * - 资源清理
     * - 异常日志记录
     * - 性能监控终点
     * - 线程上下文清理
     * 
     * 注意:ex参数可能为null(正常完成)或具体异常(处理过程中出错)
     */
    default void afterCompletion(
            HttpServletRequest request, 
            HttpServletResponse response, 
            Object handler, 
            @Nullable Exception ex) throws Exception {
    }
}

1.2 接口类图

对应关系

«interface»

HandlerInterceptor

+preHandle(request, response, handler) : boolean

+postHandle(request, response, handler, modelAndView) : void

+afterCompletion(request, response, handler, ex) : void

«interface»

AsyncHandlerInterceptor

+afterConcurrentHandlingStarted(request, response, handler) : void

«abstract»

HandlerInterceptorAdapter

+preHandle(request, response, handler) : boolean

+postHandle(request, response, handler, modelAndView) : void

+afterCompletion(request, response, handler, ex) : void

«interface»

WebRequestInterceptor

+preDispatch(request) : void

+postDispatch(request) : void

+afterCompletion(request, attributes, ex) : void

1.3 执行时序图

View ViewResolver Controller HandlerAdapter Interceptor3 Interceptor2 Interceptor1 HandlerExecutionChain DispatcherServlet HTTP Client View ViewResolver Controller HandlerAdapter Interceptor3 Interceptor2 Interceptor1 HandlerExecutionChain DispatcherServlet HTTP Client Phase 1: 前置处理(正序执行) 所有preHandle返回true 继续后续处理 Phase 2: 处理器执行 Phase 3: 后置处理(逆序执行) Phase 4: 视图解析与渲染 Phase 5: 完成处理(逆序执行) HTTP Request applyPreHandle() preHandle(1) true preHandle(2) true preHandle(3) true handle(request, response, handler) 执行Controller方法 ModelAndView ModelAndView postHandle(3) 完成 postHandle(2) 完成 postHandle(1) 完成 resolveViewName(viewName, locale) View render(model, request, response) HTML Response triggerAfterCompletion() afterCompletion(3, ex=null) 完成 afterCompletion(2, ex=null) 完成 afterCompletion(1, ex=null) 完成 HTTP Response

1.4 执行顺序核心规则

afterCompletion 执行顺序

注册顺序: A → B → C

执行顺序: C → B → A
逆序遍历

无论是否异常都执行

只执行preHandle返回true的拦截器

postHandle 执行顺序

注册顺序: A → B → C

执行顺序: C → B → A
逆序遍历

preHandle全部返回true才会执行

异常时不执行

preHandle 执行顺序

注册顺序: A → B → C

执行顺序: A → B → C
正序遍历

任何一个返回false

触发afterCompletion该拦截器及之前

跳过后续拦截器和Controller


2. 拦截器执行链完整解析

2.1 HandlerExecutionChain源码解析

public class HandlerExecutionChain {
    
    private static final Log logger = LogFactory.getLog(HandlerExecutionChain.class);
    
    // 处理器对象(Controller实例或HandlerMethod)
    private final Object handler;
    
    // 拦截器列表(有序)
    private final List<HandlerInterceptor> interceptorList = new ArrayList<>(4);
    
    // 拦截器索引(用于afterCompletion逆序执行)
    private int interceptorIndex = -1;
    
    // ==================== 前置处理 ====================
    
    /**
     * 执行所有拦截器的preHandle
     * 
     * @return true 所有拦截器都通过
     *         false 某个拦截器拒绝,继续afterCompletion后返回
     */
    boolean applyPreHandle(HttpServletRequest request, 
                           HttpServletResponse response) throws Exception {
        
        HandlerInterceptor[] interceptors = getInterceptors();
        
        if (interceptors == null) {
            return true;
        }
        
        // 【关键】记录最后执行的拦截器索引
        // 用于afterCompletion逆序执行到当前位置
        for (int i = 0; i < interceptors.length; i++) {
            HandlerInterceptor interceptor = interceptors[i];
            
            // 【关键】如果返回false
            // 1. 记录当前索引(用于逆序清理)
            // 2. 触发已通过拦截器的afterCompletion
            // 3. 返回false中断后续处理
            if (!interceptor.preHandle(request, response, this.handler)) {
                triggerAfterCompletion(request, response, null);
                return false;
            }
            
            // 【关键】记录已通过的拦截器索引
            this.interceptorIndex = i;
        }
        
        return true;
    }
    
    // ==================== 后置处理 ====================
    
    /**
     * 执行所有拦截器的postHandle
     * 
     * 注意:逆序执行,只有preHandle全部通过的才会执行到这里
     */
    void applyPostHandle(HttpServletRequest request, 
                         HttpServletResponse response, 
                         ModelAndView mv) throws Exception {
        
        HandlerInterceptor[] interceptors = getInterceptors();
        
        if (interceptors == null) {
            return;
        }
        
        // 【关键】逆序遍历
        for (int i = interceptors.length - 1; i >= 0; i--) {
            HandlerInterceptor interceptor = interceptors[i];
            interceptor.postHandle(request, response, this.handler, mv);
        }
    }
    
    // ==================== 完成处理 ====================
    
    /**
     * 执行afterCompletion
     * 
     * @param request HTTP请求
     * @param response HTTP响应
     * @param ex 处理过程中发生的异常(可能为null)
     */
    void triggerAfterCompletion(HttpServletRequest request, 
                               HttpServletResponse response, 
                               @Nullable Exception ex) {
        
        HandlerInterceptor[] interceptors = getInterceptors();
        
        if (interceptors == null) {
            return;
        }
        
        // 【关键】逆序执行,但只执行到interceptorIndex位置
        // 这是因为preHandle可能在中途失败,只需要清理已通过的
        for (int i = this.interceptorIndex; i >= 0; i--) {
            HandlerInterceptor interceptor = interceptors[i];
            
            try {
                interceptor.afterCompletion(request, response, this.handler, ex);
            } catch (Throwable ex2) {
                logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
                // 注意:afterCompletion的异常不能中断逆序清理流程
            }
        }
    }
    
    /**
     * 重载:支持已执行拦截器数量的afterCompletion
     * 用于异常处理流程
     */
    void triggerAfterCompletion(HttpServletRequest request, 
                               HttpServletResponse response, 
                               int interceptorIndex,
                               @Nullable Exception ex) {
        
        HandlerInterceptor[] interceptors = getInterceptors();
        
        if (interceptors == null) {
            return;
        }
        
        // 逆序执行到指定位置
        for (int i = interceptorIndex; i >= 0; i--) {
            HandlerInterceptor interceptor = interceptors[i];
            
            try {
                interceptor.afterCompletion(request, response, this.handler, ex);
            } catch (Throwable ex2) {
                logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
            }
        }
    }
}

2.2 preHandle返回false的影响

Client Interceptor3 Interceptor2 Interceptor1 HandlerExecutionChain DispatcherServlet Client Interceptor3 Interceptor2 Interceptor1 HandlerExecutionChain DispatcherServlet 场景:I2.preHandle返回false 【关键】只执行I1的afterCompletion I2和I3不执行 跳过Controller执行 跳过postHandle applyPreHandle() preHandle(1) true preHandle(2) false triggerAfterCompletion() afterCompletion(2) afterCompletion(1) false 返回(可能自定义响应)

2.3 异常时的afterCompletion

HandlerExceptionResolver HandlerAdapter Interceptor2 Interceptor1 HandlerExecutionChain DispatcherServlet HandlerExceptionResolver HandlerAdapter Interceptor2 Interceptor1 HandlerExecutionChain DispatcherServlet 场景:Controller抛出异常 【关键】postHandle不执行 因为异常已抛出 【关键】afterCompletion全部执行 即使发生异常 applyPreHandle() preHandle → true preHandle → true true handle() 抛出BusinessException applyPostHandle() 异常处理 ModelAndView (error视图) triggerAfterCompletion(ex) afterCompletion(ex) 完成 afterCompletion(ex) 完成

2.4 请求属性数据共享

public class AuthInterceptor implements HandlerInterceptor {
    
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        User user = authService.validateToken(request);
        request.setAttribute("currentUser", user);
        request.setAttribute("startTime", System.currentTimeMillis());
        return true;
    }
    
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, 
                          Object handler, ModelAndView mv) throws Exception {
        User user = (User) request.getAttribute("currentUser");
        if (mv != null && user != null) {
            mv.addObject("operator", user.getName());
        }
    }
    
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, 
                              Object handler, Exception ex) throws Exception {
        long duration = System.currentTimeMillis() - (Long) request.getAttribute("startTime");
        logService.recordAccess(request.getRequestURI(), duration, ex);
        UserContext.remove();
    }
}

3. 拦截器注册与配置

3.1 WebMvcConfigurer扩展点

组合使用

«interface»

WebMvcConfigurer

+addInterceptors(registry) : void

+addResourceHandlers(registry) : void

+addViewControllers(registry) : void

+configureMessageConverters(converters) : void

+addFormatters(registry) : void

+getMessageCodesResolver() : MessageCodesResolver

+getValidator() : Validator

+configureContentNegotiation(config) : void

+configureAsyncSupport(config) : void

+addCorsMappings(registry) : void

«deprecated»

WebMvcConfigurerAdapter

+所有方法默认实现

DelegatingWebMvcConfiguration

+setConfigurers(configurers) : void

3.2 InterceptorRegistry详解

public class InterceptorRegistry {
    
    // 存储所有拦截器注册信息
    private final List<InterceptorRegistration> registrations = new ArrayList<>();
    
    // 添加拦截器
    public InterceptorRegistration addInterceptor(HandlerInterceptor interceptor) {
        InterceptorRegistration registration = new InterceptorRegistration(interceptor);
        registrations.add(registration);
        return registration;
    }
    
    // 获取所有拦截器注册信息
    public List<InterceptorRegistration> getRegistrations() {
        return Collections.unmodifiableList(this.registrations);
    }
}

3.3 拦截器注册流程

InterceptorRegistration InterceptorRegistry WebMvcConfigurer RequestMappingHandlerMapping ApplicationContext SpringApplication InterceptorRegistration InterceptorRegistry WebMvcConfigurer RequestMappingHandlerMapping ApplicationContext SpringApplication 扫描所有@Controller和@RequestMapping 建立URL到HandlerMethod的映射 路径配置包括: - addPathPatterns: 包含模式 - excludePathPatterns: 排除模式 Spring容器启动 初始化RequestMappingHandlerMapping detectHandlers() 获取WebMvcConfigurer列表 addInterceptors() 创建InterceptorRegistration 配置路径模式

3.4 路径匹配规则

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
    
    @Autowired
    private AuthInterceptor authInterceptor;
    
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        
        // 认证拦截器
        registry.addInterceptor(authInterceptor)
            .addPathPatterns("/api/**")
            .excludePathPatterns("/api/login", "/api/register", "/api/public/**")
            .excludePathPatterns("/error")
            .order(1);  // order越小越先执行
    }
}
模式 匹配示例
/api/** /api, /api/users, /api/users/123
/api/users/* /api/users/123 (单层级)
/*.html /index.html
/**/*.png /images/logo.png

4. 异常处理体系全景

4.1 异常处理架构图

注解支持

具体实现类

HandlerExceptionResolver 体系

异常来源

Controller方法异常

拦截器preHandle异常

拦截器postHandle异常

视图渲染异常

HandlerExceptionResolver接口

AbstractHandlerExceptionResolver

HandlerExceptionResolverComposite

SimpleMappingExceptionResolver

DefaultHandlerExceptionResolver

ResponseStatusExceptionResolver

ExceptionHandlerExceptionResolver

ResponseEntityExceptionHandler

ExceptionHandler注解

ControllerAdvice注解

RestControllerAdvice注解

ResponseStatus注解

4.2 HandlerExceptionResolver接口

«interface»

HandlerExceptionResolver

+resolveException(request, response, handler, ex) : ModelAndView

HandlerExceptionResolverComposite

-List<HandlerExceptionResolver> resolvers

+addResolver(resolver) : void

+resolveException() : ModelAndView

«abstract»

AbstractHandlerExceptionResolver

-Log logger

-int order

-String[] mappedHandlers

-Class<?>[] mappedHandlerClasses

+shouldApplyTo(handler, request) : boolean

+logException(ex, request) : void

SimpleMappingExceptionResolver

-Properties exceptionMappings

-String defaultErrorView

-ModelMap defaultModel

-Integer defaultStatusCode

-Map<Integer~ String> statusCodes

+getMappedExceptions() : Class<?>[]

DefaultHandlerExceptionResolver

+resolveException() : ModelAndView

-ModelAndView handleTomcatError() : ...

-ModelAndView handleJettyError() : ...

-ModelAndView handleServletError() : ...

ExceptionHandlerExceptionResolver

-ContentNegotiatingViewResolver contentNegotiatingResolver

-WebApplicationContext webApplicationContext

-ArgumentResolvers argumentResolvers

+doResolveException() : ModelAndView

-getExceptionHandlerMethod() : ExceptionHandlerMethod

4.3 异常处理流程决策

ModelAndViewDefiningException

ResponseStatusException

Spring-specific异常

Controller中ExceptionHandler

其他异常

发生异常

异常类型

直接获取ModelAndView

ResponseStatusExceptionResolver

DefaultHandlerExceptionResolver

ExceptionHandlerExceptionResolver

遍历所有Resolver

渲染视图

找到匹配的Resolver?

抛出ServletException
给容器处理


5. HandlerExceptionResolver执行链

5.1 DispatcherServlet异常处理入口

// DispatcherServlet中的异常处理调用链

protected void doDispatch(HttpServletRequest request, 
                         HttpServletResponse response) {
    
    HandlerExecutionChain mappedHandler = null;
    Exception dispatchException = null;
    
    try {
        // 1. 正常处理流程...
        mappedHandler = getHandler(request);
        
        if (!mappedHandler.applyPreHandle(request, response)) {
            return;
        }
        
        // 2. Controller执行
        ModelAndView mv = ha.handle(request, response, mappedHandler.getHandler());
        
        // 3. 后置处理
        mappedHandler.applyPostHandle(request, response, mv);
        
    } catch (Exception ex) {
        // 【关键】捕获异常
        dispatchException = ex;
    } catch (Throwable err) {
        // 【关键】捕获Error
        dispatchException = new NestedServletException(
            "Handler dispatch failed", err);
    }
    
    // 4. 【关键】处理结果(包括异常)
    processDispatchResult(request, response, mappedHandler, mv, dispatchException);
}

private void processDispatchResult(HttpServletRequest request, 
                                   HttpServletResponse response,
                                   HandlerExecutionChain mappedHandler,
                                   @Nullable ModelAndView mv,
                                   @Nullable Exception exception) {
    
    boolean errorView = false;
    
    // 1. 处理异常
    if (exception != null) {
        
        if (exception instanceof ModelAndViewDefiningException) {
            // 直接定义了ModelAndView的异常
            mv = ((ModelAndViewDefiningException) exception)
                .getModelAndView();
            errorView = true;
        } else {
            // 2. 遍历HandlerExceptionResolver链
            HandlerExceptionResolver[] resolvers = getHandlerExceptionResolvers();
            
            for (HandlerExceptionResolver resolver : resolvers) {
                // 3. 调用每个Resolver尝试处理
                mv = resolver.resolveException(
                    request, response, mappedHandler.getHandler(), exception);
                
                // 4. 如果处理成功(返回非null),停止遍历
                if (mv != null) {
                    errorView = true;
                    break;
                }
            }
        }
    }
    
    // 5. 渲染视图或错误视图
    if (mv != null && !mv.wasCleared()) {
        render(mv, request, response);
    }
}

5.2 HandlerExceptionResolverComposite执行时序

Controller ExceptionHandlerExceptionResolver DefaultHandlerExceptionResolver SimpleMappingExceptionResolver HandlerExceptionResolverComposite DispatcherServlet Controller ExceptionHandlerExceptionResolver DefaultHandlerExceptionResolver SimpleMappingExceptionResolver HandlerExceptionResolverComposite DispatcherServlet 找到匹配的Resolver 停止遍历 抛出BusinessException resolveException() resolveException() null (不匹配) resolveException() null (不匹配) resolveException() ModelAndView (error视图) ModelAndView (error视图) render(error视图)

5.3 SimpleMappingExceptionResolver使用

@Configuration
public class ExceptionConfig {
    
    @Bean
    public SimpleMappingExceptionResolver simpleMappingExceptionResolver() {
        SimpleMappingExceptionResolver resolver = new SimpleMappingExceptionResolver();
        
        Properties mappings = new Properties();
        mappings.setProperty("BusinessException", "error/business");
        mappings.setProperty("ValidationException", "error/validation");
        mappings.setProperty("Exception", "error/general");
        
        resolver.setExceptionMappings(mappings);
        resolver.setDefaultErrorView("error/default");
        resolver.setExceptionAttribute("exception");  // Model中的属性名
        
        return resolver;
    }
}

5.4 DefaultHandlerExceptionResolver支持的异常

异常类型 HTTP状态码
NoHandlerFoundException 404
HttpRequestMethodNotSupportedException 405
MissingServletRequestParameterException 400
TypeMismatchException 400
HttpMessageNotReadableException 400
HttpMediaTypeNotSupportedException 415

6. @ExceptionHandler深度解析

6.1 @ExceptionHandler注解定义

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ExceptionHandler {
    
    /**
     * 处理的异常类型数组
     * 
     * 如果不指定,则处理方法参数类型的异常
     * 方法可以有多个Exception参数
     */
    Class~? extends Throwable~[] value() default {};
}

6.2 @ExceptionHandler方法签名

ExceptionHandlerExceptionResolver

-ExceptionHandlerMethodResolver methodResolver

+getExceptionHandlerMethod() : ExceptionHandlerMethod

+doResolveException() : ModelAndView

ExceptionHandlerMethodResolver

-Map<Class~? extends Throwable>, InvocableHandlerMethod<> mappedMethods

+resolveMethod(exception) : InvocableHandlerMethod

-getExceptionMapping(ex) : Class<?>

InvocableHandlerMethod

-HandlerMethodArgumentResolver[] argumentResolvers

-ParameterValueResolver parameterValueResolver

+invokeForRequest() : Object

-getMethodArgumentValues() : Object[]

6.3 支持的方法参数

支持的返回值

ModelAndView

Model

Map~String, Object~

ResponseBody返回值

ResponseEntity

void

@ExceptionHandler支持的参数

HttpServletRequest

HttpServletResponse

HttpSession

Exception ex
处理的异常

HandlerMethod
发生异常的方法

WebRequest

NativeWebRequest

Locale

OutputStream
HttpServletResponse.getOutputStream

Writer
HttpServletResponse.getWriter

Model

ModelMap

RequestHeader参数

RequestParam参数

PathVariable参数

6.4 异常匹配优先级

优先级 匹配方式 示例
1 精确匹配 @ExceptionHandler(BusinessException.class)
2 祖先类匹配 @ExceptionHandler(RuntimeException.class)
3 无参数匹配 @ExceptionHandler(Exception.class)
// 匹配优先级示例
@ExceptionHandler(BusinessException.class)     // 最优先
@ExceptionHandler(RuntimeException.class)    // 次优先  
@ExceptionHandler(Exception.class)            // 最后兜底

7. @ControllerAdvice全局异常处理

7.1 @ControllerAdvice详解

ControllerAdviceBean

-Object bean

-BeanFactory beanFactory

-String beanName

+getBean() : Object

+getBeanFactory() : BeanFactory

+equals(obj) : boolean

+hashCode() : int

+toString() : String

ExceptionHandlerExceptionResolver

-List<ControllerAdviceBean> adviceBeans

-ExceptionHandlerMethodFactory methodFactory

+initExceptionHandlerAdviceCache() : void

+getExceptionHandlerMethod() : ExceptionHandlerMethod

+shouldApplyTo() : boolean

ControllerAdviceSelector

-String[] basePackages

-Class<?>[] annotations

-Class<?>[] assignableTypes

7.2 @ControllerAdvice选择器

basePackages

annotations

assignableTypes

无配置

ControllerAdvice

配置方式

basePackages指定包名
指定包下的Controller

annotations={Target
标注特定注解的Controller

assignableTypes={BaseController.class}
继承特定类的Controller

全局生效
所有Controller

精确匹配包名

匹配标注注解的类

匹配继承层次

无限制

7.3 @ControllerAdvice注解定义

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface ControllerAdvice {
    
    /**
     * 限制@ExceptionHandler生效的包
     * 
     * 指定包及其子包下的@Controller生效
     */
    @AliasFor("basePackages")
    String[] value() default {};
    
    @AliasFor("value")
    String[] basePackages() default {};
    
    /**
     * 限制@Controller的类级别注解
     * 
     * 只有标注了这些注解的@Controller才生效
     * 例如:@RestController
     */
    Class~? extends Annotation~[] annotations() default {};
    
    /**
     * 限制@Controller的类型
     * 
     * 只有继承/实现这些类型的@Controller生效
     */
    Class~?~[] assignableTypes() default {};
}

7.4 @ModelAttribute全局数据绑定

@ControllerAdvice
public class GlobalControllerAdvice {
    
    @ModelAttribute
    public void addGlobalAttributes(Model model) {
        model.addAttribute("appName", "My Application");
        model.addAttribute("version", "1.0.0");
    }
    
    @InitBinder
    public void initBinder(WebDataBinder binder) {
        binder.registerCustomEditor(Date.class, 
            new CustomDateEditor(new SimpleDateFormat("yyyy-MM-dd"), false));
    }
    
    @ExceptionHandler(BusinessException.class)
    @ResponseBody
    public Result<Void> handleBusiness(BusinessException ex) {
        return Result.fail(ex.getCode(), ex.getMessage());
    }
    
    @ExceptionHandler(Exception.class)
    @ResponseBody
    public Result<Void> handleAll(Exception ex) {
        return Result.fail(500, "系统错误");
    }
}

7.5 @RestControllerAdvice简化写法

// @RestControllerAdvice = @ControllerAdvice + @ResponseBody
@RestControllerAdvice
public class GlobalExceptionHandler {
    
    @ExceptionHandler(UserNotFoundException.class)
    public Result<Void> handleUserNotFound(UserNotFoundException ex) {
        return Result.fail(404, ex.getMessage());
    }
    
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Result<Void> handleValidation(MethodArgumentNotValidException ex) {
        String msg = ex.getBindingResult().getFieldErrors().stream()
            .map(FieldError::getDefaultMessage)
            .collect(Collectors.joining(", "));
        return Result.fail(400, msg);
    }
    
    @ExceptionHandler(AccessDeniedException.class)
    public Result<Void> handleAccessDenied(AccessDeniedException ex) {
        return Result.fail(403, "权限不足");
    }
    
    @ExceptionHandler(Throwable.class)
    public Result<Void> handleAll(Throwable ex) {
        log.error("未处理异常", ex);
        return Result.fail(500, "系统繁忙");
    }
}

8. 异常处理流程时序

8.1 完整异常处理时序

View ViewResolver @ControllerAdvice HandlerExceptionResolver Controller HandlerAdapter HandlerExecutionChain DispatcherServlet HTTP Client View ViewResolver @ControllerAdvice HandlerExceptionResolver Controller HandlerAdapter HandlerExecutionChain DispatcherServlet HTTP Client postHandle不执行 异常已发生 HTTP Request applyPreHandle() true handle() 调用Controller方法 抛出BusinessException null ModelAndView applyPostHandle() resolveException() findExceptionHandler() handleBusinessException() invoke handler Result.fail(404) ModelAndView (JSON) triggerAfterCompletion(ex) 完成 JSON响应: {code: 404, message: "..."}

8.2 异常处理与拦截器的协同

@ExceptionHandler ExceptionHandlerExceptionResolver HandlerAdapter AuthInterceptor HandlerExecutionChain DispatcherServlet @ExceptionHandler ExceptionHandlerExceptionResolver HandlerAdapter AuthInterceptor HandlerExecutionChain DispatcherServlet 场景:Controller抛出异常 不执行postHandle 异常处理流程 afterCompletion流程 可记录异常日志 可清理资源 applyPreHandle() preHandle() - true true handle() 执行业务逻辑 抛出ServiceException applyPostHandle() resolveException() 查找ExceptionHandler 处理方法 ModelAndView (错误信息) triggerAfterCompletion(ex) afterCompletion(ex) 完成

8.3 异常处理决策流程

Controller方法

拦截器preHandle

拦截器postHandle

ExceptionHandler

ResponseStatus

Servlet标准异常

其他

发生异常

异常来源

异常类型

跳过Controller
执行afterCompletion

执行afterCompletion
继续异常处理

ExceptionHandlerExceptionResolver

ResponseStatusExceptionResolver

DefaultHandlerExceptionResolver

遍历HandlerExceptionResolver链

找到匹配方法?

执行ExceptionHandler方法

设置HTTP状态码

映射到标准HTTP异常

找到Resolver?

返回错误视图/JSON

抛出给容器处理

返回ModelAndView


9. 拦截器与异常处理协同

9.1 典型场景:认证失败与异常处理

@ControllerAdvice @ExceptionHandler ExceptionHandlerExceptionResolver AuthInterceptor DispatcherServlet HTTP Client @ControllerAdvice @ExceptionHandler ExceptionHandlerExceptionResolver AuthInterceptor DispatcherServlet HTTP Client Token验证失败 记录认证失败日志 拦截器preHandle直接响应 不走异常处理流程 POST /api/users (无Token) preHandle() false (返回401) triggerAfterCompletion() 401 Unauthorized

9.2 典型场景:业务异常与全局处理

@ExceptionHandler ExceptionHandlerExceptionResolver Controller AuthInterceptor DispatcherServlet HTTP Client @ExceptionHandler ExceptionHandlerExceptionResolver Controller AuthInterceptor DispatcherServlet HTTP Client POST /api/users (有效Token) preHandle() - true 调用业务方法 抛出UserNotFoundException applyPostHandle() - 不执行 resolveException() 查找ExceptionHandler Result(404) triggerAfterCompletion(ex) JSON: {code: 404, message: "用户不存在"}

9.3 拦截器中的异常处理

public class TransactionInterceptor implements HandlerInterceptor {
    
    @Autowired
    private TransactionManager transactionManager;
    
    @Override
    public boolean preHandle(HttpServletRequest request, 
                           HttpServletResponse response, 
                           Object handler) throws Exception {
        
        // 开启事务
        TransactionStatus status = transactionManager.getTransaction(
            new DefaultTransactionDefinition());
        
        // 将事务状态绑定到请求属性
        request.setAttribute("txStatus", status);
        
        return true;
    }
    
    @Override
    public void postHandle(HttpServletRequest request, 
                          HttpServletResponse response, 
                          Object handler, 
                          ModelAndView mv) throws Exception {
        
        // 正常完成,提交事务
        TransactionStatus status = getTransactionStatus(request);
        if (status != null && !status.isCompleted()) {
            transactionManager.commit(status);
        }
    }
    
    @Override
    public void afterCompletion(HttpServletRequest request, 
                               HttpServletResponse response, 
                               Object handler, 
                               Exception ex) throws Exception {
        
        // 【关键】无论成功还是异常,都需要处理事务
        TransactionStatus status = getTransactionStatus(request);
        if (status != null && !status.isCompleted()) {
            if (ex != null) {
                // 发生异常,回滚
                transactionManager.rollback(status);
            } else {
                // 正常完成(如果postHandle未执行到这里)
                transactionManager.commit(status);
            }
        }
        
        // 清理请求属性
        request.removeAttribute("txStatus");
    }
    
    private TransactionStatus getTransactionStatus(HttpServletRequest request) {
        return (TransactionStatus) request.getAttribute("txStatus");
    }
}

9.4 常见问题与解决方案

9.4.1 preHandle返回false后的响应处理
public class UnifiedResponseInterceptor implements HandlerInterceptor {
    
    @Override
    public boolean preHandle(HttpServletRequest request, 
                           HttpServletResponse response, 
                           Object handler) throws Exception {
        
        // 统一错误响应格式
        if (!validateRequest(request)) {
            return sendError(response, 400, "请求参数不合法");
        }
        
        if (!authenticate(request)) {
            return sendError(response, 401, "未授权访问");
        }
        
        return true;
    }
    
    private boolean sendError(HttpServletResponse response, int code, String message) 
            throws IOException {
        response.setStatus(code);
        response.setContentType("application/json;charset=UTF-8");
        Result<Void> result = Result.fail(code, message);
        response.getWriter().write(new ObjectMapper().writeValueAsString(result));
        return false;
    }
}
9.4.2 afterCompletion异常不中断清理流程
public class ResourceCleanupInterceptor implements HandlerInterceptor {
    
    @Override
    public void afterCompletion(HttpServletRequest request, 
                               HttpServletResponse response, 
                               Object handler, 
                               Exception ex) throws Exception {
        // 注意:afterCompletion的异常不能中断清理流程
        try {
            // 清理数据库连接
            Connection connection = getConnection(request);
            if (connection != null && !connection.isClosed()) {
                connection.close();
            }
        } catch (SQLException e) {
            log.error("关闭数据库连接失败", e);  // 只记录,不抛出
        }
        
        try {
            ThreadLocalHolder.clear();  // 清理ThreadLocal
        } catch (Exception e) {
            log.error("清理ThreadLocal失败", e);
        }
    }
}
9.4.3 拦截器之间的数据共享
// 拦截器1:设置共享数据
public class ContextInterceptor implements HandlerInterceptor {
    
    @Override
    public boolean preHandle(HttpServletRequest request, 
                           HttpServletResponse response, 
                           Object handler) throws Exception {
        RequestContext context = new RequestContext();
        context.setRequestId(UUID.randomUUID().toString());
        context.setStartTime(System.currentTimeMillis());
        request.setAttribute("__CONTEXT__", context);
        return true;
    }
}

// 拦截器2:使用共享数据
public class AuditInterceptor implements HandlerInterceptor {
    
    @Override
    public void afterCompletion(HttpServletRequest request, 
                               HttpServletResponse response, 
                               Object handler, 
                               Exception ex) throws Exception {
        RequestContext context = (RequestContext) request.getAttribute("__CONTEXT__");
        if (context != null) {
            long duration = System.currentTimeMillis() - context.getStartTime();
            auditService.record(context.getRequestId(), request.getRequestURI(), duration, ex);
        }
    }
}
9.4.4 REST API 与 MVC 的异常处理差异
场景 REST API MVC
返回格式 JSON 视图
使用注解 @RestControllerAdvice @ControllerAdvice
返回类型 ResponseEntity, Result ModelAndView, String
状态码 HTTP状态码 错误视图
// REST API 异常处理
@RestControllerAdvice
public class RestExceptionHandler {
    @ExceptionHandler(BusinessException.class)
    public Result<Void> handleBusiness(BusinessException ex) {
        return Result.fail(ex.getCode(), ex.getMessage());
    }
}

// MVC 异常处理
@ControllerAdvice
public class MvcExceptionHandler {
    @ExceptionHandler(BusinessException.class)
    public String handleBusiness(BusinessException ex, Model model) {
        model.addAttribute("error", ex.getMessage());
        return "error/business";
    }
}
9.4.5 拦截器中的异步处理
// 异步HandlerInterceptor
public class AsyncInterceptor implements AsyncHandlerInterceptor {
    
    @Override
    public void afterConcurrentHandlingStarted(HttpServletRequest request, 
                                              HttpServletResponse response, 
                                              Object handler) {
        // 释放主线程持有的资源
        releaseResources(request);
    }
}
9.4.6 拦截器与过滤器的区别
特性 Filter HandlerInterceptor
来源 Servlet API Spring MVC
执行时机 DispatcherServlet之前 DispatcherServlet内部
IOC容器 不属于 属于Spring IOC容器
异常处理 FilterChain.doFilter外 @ExceptionHandler处理
典型用途 字符编码、XSS、CORS 认证授权、日志、性能监控
// Filter:处理请求编码
@WebFilter("/*")
public class EncodingFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) 
            throws IOException, ServletException {
        request.setCharacterEncoding("UTF-8");
        chain.doFilter(request, response);
    }
}

// Interceptor:处理认证
@Component
public class AuthInterceptor implements HandlerInterceptor {
    @Autowired
    private UserService userService;  // 可直接注入Spring Bean
    
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        return userService.getCurrentUser() != null;
    }
}
9.4.7 性能监控拦截器
@Component
public class PerformanceInterceptor implements HandlerInterceptor {
    
    private static final long SLOW_REQUEST_THRESHOLD = 1000;
    
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        request.setAttribute("__START_TIME__", System.currentTimeMillis());
        return true;
    }
    
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, 
                              Object handler, Exception ex) throws Exception {
        long duration = System.currentTimeMillis() - (Long) request.getAttribute("__START_TIME__");
        
        if (duration > SLOW_REQUEST_THRESHOLD) {
            log.warn("慢请求: {} {} - {}ms", request.getMethod(), request.getRequestURI(), duration);
        }
        
        log.info("请求完成: {} {} - {}ms - {}", 
            request.getMethod(), request.getRequestURI(), duration, response.getStatus());
    }
}

10. 实战:完整登录认证方案

10.1 项目结构

com.example.web

config/

interceptor/

controller/

exception/

WebMvcConfig.java

AuthInterceptor.java

GlobalExceptionHandler.java

10.2 统一响应结果

public class Result<T> {
    private int code;
    private String message;
    private T data;
    private long timestamp = System.currentTimeMillis();
    
    public static <T> Result<T> success(T data) {
        Result<T> r = new Result<>();
        r.code = 200;
        r.message = "success";
        r.data = data;
        return r;
    }
    
    public static <T> Result<T> fail(int code, String message) {
        Result<T> r = new Result<>();
        r.code = code;
        r.message = message;
        return r;
    }
}

public class BusinessException extends RuntimeException {
    private final int code;
    public BusinessException(int code, String message) { super(message); this.code = code; }
    public int getCode() { return code; }
}

10.3 认证拦截器

@Component
public class AuthInterceptor implements HandlerInterceptor {
    
    @Autowired
    private UserService userService;
    
    @Override
    public boolean preHandle(HttpServletRequest request, 
                            HttpServletResponse response, 
                            Object handler) throws Exception {
        
        // 放行OPTIONS预检请求
        if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {
            return true;
        }
        
        // 获取Token并验证
        String token = extractToken(request);
        if (token == null) {
            return sendError(response, 401, "请先登录");
        }
        
        User user = userService.validateToken(token);
        if (user == null) {
            return sendError(response, 401, "Token无效或已过期");
        }
        
        // 设置用户上下文
        UserContext.setCurrentUser(user);
        request.setAttribute("currentUser", user);
        
        return true;
    }
    
    @Override
    public void afterCompletion(HttpServletRequest request, 
                              HttpServletResponse response, 
                              Object handler, 
                              Exception ex) throws Exception {
        UserContext.remove();
    }
    
    private String extractToken(HttpServletRequest request) {
        String token = request.getHeader("Authorization");
        if (token != null && token.startsWith("Bearer ")) {
            return token.substring(7);
        }
        return request.getParameter("token");
    }
    
    private boolean sendError(HttpServletResponse response, int code, String message) 
            throws IOException {
        response.setStatus(code);
        response.setContentType("application/json;charset=UTF-8");
        Result<Void> result = Result.fail(code, message);
        response.getWriter().write(new ObjectMapper().writeValueAsString(result));
        return false;
    }
}

10.4 全局异常处理

@RestControllerAdvice(basePackages = "com.example.web.controller")
public class GlobalExceptionHandler {
    
    @ExceptionHandler(BusinessException.class)
    public Result<Void> handleBusiness(BusinessException ex) {
        return Result.fail(ex.getCode(), ex.getMessage());
    }
    
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Result<Void> handleValidation(MethodArgumentNotValidException ex) {
        String msg = ex.getBindingResult().getFieldErrors().stream()
            .map(e -> e.getField() + ": " + e.getDefaultMessage())
            .collect(Collectors.joining("; "));
        return Result.fail(400, msg);
    }
    
    @ExceptionHandler(AccessDeniedException.class)
    public Result<Void> handleAccessDenied(AccessDeniedException ex) {
        return Result.fail(403, "权限不足");
    }
    
    @ExceptionHandler(NoHandlerFoundException.class)
    public Result<Void> handleNotFound(NoHandlerFoundException ex) {
        return Result.fail(404, "资源不存在");
    }
    
    @ExceptionHandler(Exception.class)
    public Result<Void> handleAll(Exception ex) {
        log.error("未处理异常", ex);
        return Result.fail(500, "系统繁忙,请稍后重试");
    }
}

10.5 限流拦截器

@Component
public class RateLimitInterceptor implements HandlerInterceptor {
    
    private final Map<String, AtomicInteger> counters = new ConcurrentHashMap<>();
    private final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
    
    @Value("${rate.limit.max-requests:100}")
    private int maxRequests;
    
    @Value("${rate.limit.window-seconds:60}")
    private int windowSeconds;
    
    @PostConstruct
    public void init() {
        // 每分钟清空计数器
        scheduler.scheduleAtFixedRate(() -> {
            counters.clear();
            log.info("限流计数器已重置");
        }, windowSeconds, windowSeconds, TimeUnit.SECONDS);
    }
    
    @PreDestroy
    public void destroy() {
        scheduler.shutdown();
    }
    
    @Override
    public boolean preHandle(HttpServletRequest request, 
                           HttpServletResponse response, 
                           Object handler) throws Exception {
        
        // 获取客户端标识(优先使用用户ID,否则使用IP)
        String clientKey = getClientKey(request);
        
        // 获取或创建计数器
        AtomicInteger counter = counters.computeIfAbsent(clientKey, 
            k -> new AtomicInteger(0));
        
        // 原子递增
        int currentCount = counter.incrementAndGet();
        
        // 检查是否超过限制
        if (currentCount > maxRequests) {
            log.warn("请求限流触发: client={}, count={}, limit={}", 
                clientKey, currentCount, maxRequests);
            
            writeRateLimitResponse(response, maxRequests, windowSeconds);
            return false;
        }
        
        // 设置限流信息到响应头
        response.setHeader("X-RateLimit-Limit", String.valueOf(maxRequests));
        response.setHeader("X-RateLimit-Remaining", 
            String.valueOf(Math.max(0, maxRequests - currentCount)));
        response.setHeader("X-RateLimit-Reset", 
            String.valueOf(System.currentTimeMillis() / 1000 + windowSeconds));
        
        return true;
    }
    
    private String getClientKey(HttpServletRequest request) {
        // 优先使用登录用户ID
        User user = UserContext.getCurrentUser();
        if (user != null) {
            return "user:" + user.getId();
        }
        
        // 其次使用IP
        String ip = getClientIp(request);
        return "ip:" + ip;
    }
    
    private String getClientIp(HttpServletRequest request) {
        String ip = request.getHeader("X-Forwarded-For");
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        return ip.split(",")[0].trim();
    }
    
    private void writeRateLimitResponse(HttpServletResponse response, 
                                       int limit, 
                                       int window) throws IOException {
        response.setStatus(429);
        response.setContentType("application/json;charset=UTF-8");
        
        Result<Void> result = Result.fail(429, 
            String.format("请求过于频繁,请%d秒后重试", window));
        
        response.getWriter().write(new ObjectMapper().writeValueAsString(result));
    }
}

10.6 完整配置类

@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
    
    @Autowired
    private TenantInterceptor tenantInterceptor;
    @Autowired
    private AuthInterceptor authInterceptor;
    @Autowired
    private RateLimitInterceptor rateLimitInterceptor;
    @Autowired
    private LoggingInterceptor loggingInterceptor;
    @Autowired
    private PerformanceInterceptor performanceInterceptor;
    
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        
        // 1. 租户拦截器(最先执行)
        registry.addInterceptor(tenantInterceptor)
            .addPathPatterns("/api/**").order(1);
        
        // 2. 限流拦截器
        registry.addInterceptor(rateLimitInterceptor)
            .addPathPatterns("/api/**")
            .excludePathPatterns("/api/public/**").order(2);
        
        // 3. 认证拦截器
        registry.addInterceptor(authInterceptor)
            .addPathPatterns("/api/**")
            .excludePathPatterns("/api/login", "/api/register").order(3);
        
        // 4. 性能监控拦截器
        registry.addInterceptor(performanceInterceptor)
            .addPathPatterns("/api/**").order(4);
        
        // 5. 日志拦截器(最后执行)
        registry.addInterceptor(loggingInterceptor)
            .addPathPatterns("/api/**").order(5);
    }
}

10.7 执行流程总结

GlobalExceptionHandler Controller TransactionInterceptor PerformanceInterceptor AuthInterceptor RateLimitInterceptor TenantInterceptor HTTP Client GlobalExceptionHandler Controller TransactionInterceptor PerformanceInterceptor AuthInterceptor RateLimitInterceptor TenantInterceptor HTTP Client alt [Controller正常] [Controller异常] 异常时postHandle不执行 但afterCompletion都会执行 preHandle (order=1) 设置租户上下文 preHandle (order=2) preHandle (order=3) preHandle (order=4) preHandle (order=5) 执行Controller 正常返回 postHandle postHandle postHandle postHandle 抛出异常 统一错误响应 afterCompletion afterCompletion afterCompletion afterCompletion 响应

总结

拦截器执行流程

preHandle返回false

跳过后续拦截器

跳过Controller

执行已通过拦截器的afterCompletion

执行顺序

preHandle: 正序

postHandle: 逆序

afterCompletion: 逆序

HandlerInterceptor三方法

preHandle
Controller前执行
返回boolean

postHandle
Controller后、视图前
无返回值

afterCompletion
始终执行
接收异常参数

异常处理体系

组件 职责 处理范围
HandlerExceptionResolver 异常解析接口 所有异常
SimpleMappingExceptionResolver 视图异常映射 视图异常
DefaultHandlerExceptionResolver Servlet标准异常 HTTP状态异常
ExceptionHandlerExceptionResolver @ExceptionHandler 业务异常
@ExceptionHandler 方法级异常处理 Controller内
@ControllerAdvice 全局异常处理 所有Controller

核心要点

  1. 拦截器preHandle返回false:跳过后续处理,直接触发afterCompletion
  2. postHandle不执行时机:Controller抛出异常时不执行
  3. afterCompletion始终执行:无论成功还是异常,都会逆序执行
  4. 异常处理链遍历:按顺序遍历HandlerExceptionResolver,找到第一个处理的为止
  5. @ExceptionHandler优先级:精确匹配 > 祖先类匹配 > 无参数匹配
  6. @ControllerAdvice选择器:可通过basePackages、annotations、assignableTypes限制范围
  7. 拦截器与异常处理协同:拦截器处理认证失败直接响应,异常处理处理业务异常
Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐