【Spring Boot】谁转发了我的错误?

用了Spring Boot之后知道错误信息会在 ErrorController下渲染,但默认的ErrorController只是一个简单普通的Controller,有一个简单的入口 /error(默认情况下),那么发生异常时为什么会进入这个入口呢?

来到Spring Boot默认的异常配置ErrorMvcAutoConfiguration,可以看到一个自定义的错误页面的配置

@Bean
public ErrorPageCustomizer errorPageCustomizer(DispatcherServletPath dispatcherServletPath) {
    return new ErrorPageCustomizer(this.serverProperties, dispatcherServletPath);
}

这个ErrorPageCustomizer实际上是一个ErrorPageRegistrar,用来注册错误页面的,看下ErrorPageCustomizer的源码:

@Override
public void registerErrorPages(ErrorPageRegistry errorPageRegistry) {
    ErrorPage errorPage = new ErrorPage(
            this.dispatcherServletPath.getRelativePath(this.properties.getError().getPath()));
    errorPageRegistry.addErrorPages(errorPage);
}

可以看到,它尝试注册了默认的错误入口 /error ,并且这个路径指向了ErrorController,但实际负责注册的是谁?

看下ErrorPageRegistry的实现类,可以看到一个熟悉的身影: TomcatServletWebServerFactory,在通过jar方式启动时,会通过这个类注册错误页面,这个时候由tomcat负责转发:

for (ErrorPage errorPage : getErrorPages()) {
    org.apache.tomcat.util.descriptor.web.ErrorPage tomcatErrorPage = new org.apache.tomcat.util.descriptor.web.ErrorPage();
    tomcatErrorPage.setLocation(errorPage.getPath());
    tomcatErrorPage.setErrorCode(errorPage.getStatusCode());
    tomcatErrorPage.setExceptionType(errorPage.getExceptionName());
    context.addErrorPage(tomcatErrorPage);
}

在通过war方式启动的时候,没有servlet api可以提供错误页面注册,那么这个时候就由Spring Boot来负责错误的转发(通过ErrorPageFilter)