yudaocode/SpringBoot-Labs · warning · RuntimeException

故意抛个错误

Error message

故意抛个错误

What it means

A RuntimeException('故意抛个错误') thrown inside ThirdInterceptor.afterCompletion, i.e. after the handler finished and the view/response processing completed. Spring MVC guarantees afterCompletion runs even on exception, but any exception thrown from it arrives after the response is essentially done: it cannot change the response body and is only logged (typically at WARN by DispatcherServlet's handler-interceptor cleanup). This lab endpoint exists to demonstrate that interceptor afterCompletion failures are mostly invisible to the client.

Source

Thrown at lab-23/lab-springmvc-23-02/src/main/java/cn/iocoder/springboot/lab23/springmvc/core/interceptor/ThirdInterceptor.java:29

public class ThirdInterceptor implements HandlerInterceptor {

    private Logger logger = LoggerFactory.getLogger(getClass());

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        logger.info("[preHandle][handler({})]", handler);
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        logger.info("[postHandle][handler({})]", handler);
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        logger.info("[afterCompletion][handler({})]", handler, ex);
        throw new RuntimeException("故意抛个错误"); // 故意抛出异常
    }

}

View on GitHub (pinned to 6c12efaed0)

Solutions

  1. Never let afterCompletion throw: wrap its body in try-catch and only log, since the response is already committed and you cannot inform the client.
  2. Delete this deliberate throw (comment '故意抛出异常') once the demo behavior is verified.
  3. If cleanup can fail, make it idempotent and guard each resource individually so one failure does not skip the rest.
  4. Check logs (not the HTTP response) when verifying this demo: the client still receives the successful response.

Example fix

// before
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
    logger.info("[afterCompletion][handler({})]", handler, ex);
    throw new RuntimeException("故意抛个错误");
}

// after
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
    try {
        logger.info("[afterCompletion][handler({})]", handler, ex);
    } catch (Exception cleanupEx) {
        logger.warn("[afterCompletion][cleanup failed]", cleanupEx);
    }
}
Defensive patterns

Strategy: try-catch

Try / catch

// Inside afterCompletion itself — the only correct place:
@Override
public void afterCompletion(HttpServletRequest req, HttpServletResponse res, Object handler, Exception ex) {
    try {
        // cleanup logic
    } catch (Exception cleanupEx) {
        logger.warn("afterCompletion cleanup failed", cleanupEx);
    }
}

Prevention

When it happens

Trigger: Any request that traverses a HandlerMapping this interceptor is registered on, once the interceptor's afterCompletion is invoked — i.e., after every handled request, success or failure. The throw is unconditional in this demo.

Common situations: Developers put cleanup (resource release, metric recording) in afterCompletion and that cleanup itself throws (NPE on a null resource, closed connection). The client sees a normal 200 while the log fills with interceptor exceptions; in older Spring Boot versions, repeated afterCompletion exceptions can mask the original handler exception in logs.

Related errors


AI-assisted analysis of yudaocode/SpringBoot-Labs@6c12efaed0 (2026-08-14). Data as JSON: /api/errors/62454bdd348de6d5. Report an issue: GitHub.