spring boot 全局异常处理捕获资源未找到异常(404)的核心在于区分“控制器内部抛出的业务异常”与“框架底层拦截的 http 状态码”。对于控制器中手动抛出的 `resourcenotfoundexception`,使用 @controlleradvice 配合 @exceptionhandler 即可直接拦截;而对于访问不存在的 url 触发的默认 404,由于它由 spring mvc 的 dispatcherservlet 在请求映射阶段直接返回,通常不会触发标准的异常处理链,需要通过自定义 errorcontroller 或配置 simplemappingexceptionresolver 来统一接管响应格式。

@ControllerAdvice 如何拦截自定义的业务异常
当应用逻辑中发现资源不存在时,主动抛出继承自 RuntimeException 的自定义异常,全局异常处理器能精准捕获并转换为 JSON 响应。通过定义一个专门的异常类如 ResourceNotFoundException,并在服务层抛出,控制器层的 @ExceptionHandler 方法就能接收该实例,进而构建包含错误码和消息的标准响应体。这种方式适用于所有已知业务场景下的资源缺失判断。

package com.example.demo;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.Map;
// 1. 定义自定义异常
class ResourceNotFoundException extends RuntimeException {
public ResourceNotFoundException(String message) {
super(message);
}
}
// 2. 全局异常处理器
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(ResourceNotFoundException.class)
public ResponseEntity<Map<String, Object>> handleResourceNotFound(ResourceNotFoundException ex) {
Map<String, Object> body = new HashMap<>();
body.put("error", "NOT_FOUND");
body.put("message", ex.getMessage());
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(body);
}
}
// 3. 示例控制器
@RestController
@RequestMapping("/api")
class DemoController {
@GetMapping("/items/{id}")
public String getItem(@PathVariable Long id) {
if (id == null || id <= 0) {
throw new ResourceNotFoundException("Item with id " + id + " not found");
}
return "Item " + id;
}
}
如何处理 Spring MVC 默认的 404 路径未匹配异常
当用户访问一个完全不在 @RequestMapping 映射范围内的 URL 时,Spring MVC 会抛出 NoHandlerFoundException 或直接返回 404 状态码而不经过常规的异常处理器链。为了统一这些底层异常的响应格式,需要实现 ErrorController 接口并重写 getErrorPath 方法,或者在 application.properties 中启用 spring.mvc.throw-exception-if-no-handler-found=true 并将 spring.web.resources.add-mappings=false,从而将 404 转化为可被 @ExceptionHandler 捕获的异常对象。推荐做法是实现 ErrorController 以接管所有错误路径的响应。

package com.example.demo;
import org.springframework.boot.web.servlet.error.ErrorController;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;
@Controller
public class CustomErrorController implements ErrorController {
private static final String ERROR_PATH = "/error";
@RequestMapping(ERROR_PATH)
public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
Map<String, Object> body = new HashMap<>();
Integer statusCode = (Integer) request.getAttribute("javax.servlet.error.status_code");
String errorMessage = (String) request.getAttribute("javax.servlet.error.message");
if (statusCode != null && statusCode == HttpStatus.NOT_FOUND.value()) {
body.put("error", "NOT_FOUND");
body.put("message", "The requested resource is not available.");
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(body);
}
// 其他错误处理...
body.put("error", "UNKNOWN");
body.put("message", "An unexpected error occurred.");
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(body);
}
@Override
public String getErrorPath() {
return ERROR_PATH;
}
}
当 NoHandlerFoundException 未被启用时默认行为是什么
如果未在配置文件中显式开启 throw-exception-if-no-handler-found,Spring Boot 默认会将无法匹配的请求视为静态资源查找失败,直接由内置的 Tomcat 容器返回标准的 HTML 404 页面,此时 @ControllerAdvice 和自定义 ErrorController 均无法介入,因为请求从未进入 Spring MVC 的 DispatcherServlet 核心流程进行异常分发。这意味着开发者必须明确配置属性才能将这种“静默”的 404 转化为可编程处理的异常事件,否则 API 客户端将收到非预期的 HTML 内容而非 JSON 结构。


















