在springboot中默認有一個異常處理器接口ErrorContorller
,該接口提供了getErrorPath()
方法,此接口的BasicErrorController
實現類實現了getErrorPath()
方法,以下:html
/* * AbstractErrorController是ErrorContoller的實現類 */ @Controller @RequestMapping("${server.error.path:${error.path:/error}}") public class BasicErrorController extends AbstractErrorController { private final ErrorProperties errorProperties; ... @Override public String getErrorPath() { return this.errorProperties.getPath(); } @RequestMapping(produces = MediaType.TEXT_HTML_VALUE) public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) { HttpStatus status = getStatus(request); Map<String, Object> model = Collections .unmodifiableMap(getErrorAttributes(request, isIncludeStackTrace(request, MediaType.TEXT_HTML))); response.setStatus(status.value()); ModelAndView modelAndView = resolveErrorView(request, response, status, model); return (modelAndView != null) ? modelAndView : new ModelAndView("error", model); } @RequestMapping public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) { HttpStatus status = getStatus(request); if (status == HttpStatus.NO_CONTENT) { return new ResponseEntity<>(status); } Map<String, Object> body = getErrorAttributes(request, isIncludeStackTrace(request, MediaType.ALL)); return new ResponseEntity<>(body, status); } .... }
errorProperties
類定義以下:spring
public class ErrorProperties { /** * Path of the error controller. */ @Value("${error.path:/error}") private String path = "/error"; ... }
因而可知,springboot中默認有一個處理/error
映射的控制器,有error
和errorHtml
兩個方法的存在,它能夠處理來自瀏覽器頁面和來自機器客戶端(app應用)的請求。瀏覽器
當用戶請求不存在的url時,dispatcherServlet
會交由ResourceHttpRequestHandler
映射處理器來處理該請求,並在handlerRequest
方法中,重定向至/error
映射,代碼以下:springboot
@Override public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // For very general mappings (e.g. "/") we need to check 404 first Resource resource = getResource(request); if (resource == null) { logger.debug("Resource not found"); response.sendError(HttpServletResponse.SC_NOT_FOUND); // 404 return; } ... }
getResource(request)
會調用this.resolverChain.resolveResource(request, path, getLocations())
方法,getLocations()
方法返回結果以下:app
接着程序會執行到getResource(pathToUse, location)
方法以下:ide
@Nullable protected Resource getResource(String resourcePath, Resource location) throws IOException { // 新建一個resource對象,url爲 location + resourcePath,判斷此對象(文件)是否存在 Resource resource = location.createRelative(resourcePath); if (resource.isReadable()) { if (checkResource(resource, location)) { return resource; } else if (logger.isWarnEnabled()) { Resource[] allowedLocations = getAllowedLocations(); logger.warn("Resource path \"" + resourcePath + "\" was successfully resolved " + "but resource \"" + resource.getURL() + "\" is neither under the " + "current location \"" + location.getURL() + "\" nor under any of the " + "allowed locations " + (allowedLocations != null ? Arrays.asList(allowedLocations) : "[]")); } } return null; }
在resource.isReadable()
中,程序會在locations目錄中尋找path目錄下文件,因爲不存在,返回null。源碼分析
最終也就致使程序重定向至/error映射,若是是來自瀏覽器的請求,也就會返回/template/error/404.html
頁面,因此對於404請求,只須要在template目錄下新建error目錄,放入404頁面便可。this
ControllerAdvice
註解 + ExceptionHandler
註解來助理不一樣錯誤類型的異常,但在springboot中404異常和攔截器異常由spring本身處理。