环境:Springboot3.0.5
错误消息格式
有如下接口:
public class DemoController {public Object index() {System.out.println(1 / 0) ;return "/demo/index" ;}}
当访问上面接口后,默认情况下Springboot会返回如下错误信息:

当请求的Accept是text/html返回的是HTML结果,当Accpet是application/json返回如下:

后台接口会根据不同的Accept返回不同的数据格式。
错误处理原理
Springboot在启动过程中会执行如下处理:
public abstract class AbstractApplicationContext {public void refresh() {onRefresh();}}
ServletWebServerApplicationContext
public class ServletWebServerApplicationContext {protected void onRefresh() {super.onRefresh();try {// 创建web服务createWebServer();} catch (Throwable ex) {throw new ApplicationContextException("Unable to start web server", ex);}}private void createWebServer() {// 这里假设我们使用的是Tomcat容器,那么这里的factory = TomcatServletWebServerFactorythis.webServer = factory.getWebServer(getSelfInitializer());}}
TomcatServletWebServerFactory
public class TomcatServletWebServerFactory {public WebServer getWebServer(ServletContextInitializer... initializers) {// 创建Tomcat实例Tomcat tomcat = new Tomcat();// ...// 准备上下文prepareContext(tomcat.getHost(), initializers);return getTomcatWebServer(tomcat);}protected void prepareContext(Host host, ServletContextInitializer[] initializers) {// 该类继承自StandardContext类(该类所属tomcat,每一个StandardContext代表了一个webapp)TomcatEmbeddedContext context = new TomcatEmbeddedContext();// ...// 配置上下文configureContext(context, initializersToUse);}// 配置上下文protected void configureContext(Context context, ServletContextInitializer[] initializers) {// 获取当前Spring容器中配置的ErrorPage,然后注册到Tomcat当前webapp的上下文中// 这里其实对应的就是web.xml中配置的错误页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);}}}
TomcatServletWebServerFactory类实现了ErrorPageRegistry接口,有如下方法:
// TomcatServletWebServerFactory继承自AbstractConfigurableWebServerFactory// ConfigurableWebServerFactory接口继承了ErrorPageRegistry接口public abstract class AbstractConfigurableWebServerFactory implements ConfigurableWebServerFactory {public void addErrorPages(ErrorPage... errorPages) {this.errorPages.addAll(Arrays.asList(errorPages));}}
下面查看上面的addErrorPages方法是如何被调用的。
在自动配置类中导入了下面的类
({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class})public class ServletWebServerFactoryAutoConfiguration {}// BeanPostProcessorsRegistrar实现了ImportBeanDefinitionRegistrar,用来注册Beanpublic static class BeanPostProcessorsRegistrar implements ImportBeanDefinitionRegistrar, BeanFactoryAware {public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {// 注册了BeanPostProcessor类ErrorPageRegistrarBeanPostProcessorregisterSyntheticBeanIfMissing(registry, "errorPageRegistrarBeanPostProcessor", ErrorPageRegistrarBeanPostProcessor.class);}}public class ErrorPageRegistrarBeanPostProcessor implements BeanPostProcessor, BeanFactoryAware {public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {// 判断当前的Bean是否实现了ErrorPageRegistryif (bean instanceof ErrorPageRegistry errorPageRegistry) {postProcessBeforeInitialization(errorPageRegistry);}return bean;}private void postProcessBeforeInitialization(ErrorPageRegistry registry) {// 遍历所有的ErrorPageRegistrar,注册到当前实现了ErrorPageRegistry接口的Bean中// 如上面的TomcatServletWebServerFactoryfor (ErrorPageRegistrar registrar : getRegistrars()) {registrar.registerErrorPages(registry);}}private Collection<ErrorPageRegistrar> getRegistrars() {if (this.registrars == null) {// 获取容器中所有的ErrorPageRegistrar Bean对象this.registrars = new ArrayList<>(this.beanFactory.getBeansOfType(ErrorPageRegistrar.class, false, false).values());this.registrars.sort(AnnotationAwareOrderComparator.INSTANCE);this.registrars = Collections.unmodifiableList(this.registrars);}return this.registrars;}}
接下来就是从容器中获取ErrorPageRegistrar,然后注册到ErrorPageRegistry中
在如下自动配置类中定义了一个默认的错误也对象
public class ErrorMvcAutoConfiguration {public ErrorPageCustomizer errorPageCustomizer(DispatcherServletPath dispatcherServletPath) {return new ErrorPageCustomizer(this.serverProperties, dispatcherServletPath);}static class ErrorPageCustomizer implements ErrorPageRegistrar, Ordered {// springboot配置文件中的server接口中获取error配置信息private final ServerProperties properties;private final DispatcherServletPath dispatcherServletPath;protected ErrorPageCustomizer(ServerProperties properties, DispatcherServletPath dispatcherServletPath) {this.properties = properties;this.dispatcherServletPath = dispatcherServletPath;}public void registerErrorPages(ErrorPageRegistry errorPageRegistry) {// 获取server.error.path配置属性ErrorPage errorPage = new ErrorPage(this.dispatcherServletPath.getRelativePath(this.properties.getError().getPath()));errorPageRegistry.addErrorPages(errorPage);}}}(prefix = "server", ignoreUnknownFields = true)public class ServerProperties {private final ErrorProperties error = new ErrorProperties();}public class ErrorProperties {// 读取error.path配置,如果没有配置,则默认是/error("${error.path:/error}")private String path = "/error";}
通过上面的分析,默认情况下springboot容器启动后会像tomcat容器中注册一个/error的错误页,这个/error又是谁呢?
默认错误Controller
在默认的错误页自动配置中
public class ErrorMvcAutoConfiguration {public DefaultErrorAttributes errorAttributes() {return new DefaultErrorAttributes();}// 该Controller就是默认的/error错误跳转的类public BasicErrorController basicErrorController(ErrorAttributes errorAttributes, ObjectProvider<ErrorViewResolver> errorViewResolvers) {return new BasicErrorController(errorAttributes, this.serverProperties.getError(),errorViewResolvers.orderedStream().toList());}}public class BasicErrorController extends AbstractErrorController {// 当请求的Accept=text/html时调用该方法public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {// ...}// 当请求的Accept=application/json时调用该方法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, getErrorAttributeOptions(request, MediaType.ALL));return new ResponseEntity<>(body, status);}}
以上就是当springboot默认情况下发生错误时的执行输出原理。
完毕!!!
求个关注



