Spring Boot錯誤處理

Spring Boot提供了兩種類型的錯誤處理機制,一種是依賴于內(nèi)嵌容器的ErrorPage機制;另外一種是基于Spring Mvc的異常處理機制;

錯誤配置類

Spring Boot錯誤處理相關的自動配置主要是通過ErrorMvcAutoConfiguration實現(xiàn)的:

@Bean
@ConditionalOnMissingBean(value = ErrorAttributes.class, search = SearchStrategy.CURRENT)
public DefaultErrorAttributes errorAttributes() {
    return new DefaultErrorAttributes();
}

@Bean
@ConditionalOnMissingBean(value = ErrorController.class, search = SearchStrategy.CURRENT)
public BasicErrorController basicErrorController(ErrorAttributes errorAttributes) {
    return new BasicErrorController(errorAttributes, this.serverProperties.getError(),
            this.errorViewResolvers);
}

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

@Bean
@ConditionalOnBean(DispatcherServlet.class)
@ConditionalOnMissingBean
public DefaultErrorViewResolver conventionErrorViewResolver() {
    return new DefaultErrorViewResolver(this.applicationContext,
            this.resourceProperties);
}

首先來看看DefaultErrorAttributes:

@Order(Ordered.HIGHEST_PRECEDENCE)
public class DefaultErrorAttributes
    implements ErrorAttributes, HandlerExceptionResolver, Ordered{
    @Override
    public ModelAndView resolveException(HttpServletRequest request,
            HttpServletResponse response, Object handler, Exception ex) {
        storeErrorAttributes(request, ex);
        return null;
    }
}

  • DefaultErrorAttributes實現(xiàn)了HandlerExceptionResolver接口,且為最高優(yōu)先級,意味著當Spring MVC發(fā)生錯誤時,首先會由它進行處理;
  • resolveException方法將異常對象保存到request中;
  • 關于HandlerExceptionResolver是如何生效的,可以查看DispatcherServlet的相關代碼;

ErrorPage機制介紹

接下來看看ErrorMvcAutoConfiguration中定義的ErrorPageCustomizer,這是ErrorPage錯誤處理比較關鍵的地方:

首先看內(nèi)嵌容器的自動配置類EmbeddedServletContainerAutoConfiguration:

@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
        BeanDefinitionRegistry registry) {
    if (this.beanFactory == null) {
        return;
    }
    registerSyntheticBeanIfMissing(registry,
            "embeddedServletContainerCustomizerBeanPostProcessor",
            EmbeddedServletContainerCustomizerBeanPostProcessor.class);
    registerSyntheticBeanIfMissing(registry,
            "errorPageRegistrarBeanPostProcessor",
            ErrorPageRegistrarBeanPostProcessor.class);
}

Spring Boot注冊了ErrorPageRegistrarBeanPostProcessor

@Override
public Object postProcessBeforeInitialization(Object bean, String beanName)
        throws BeansException {
    if (bean instanceof ErrorPageRegistry) {
        postProcessBeforeInitialization((ErrorPageRegistry) bean);
    }
    return bean;
}


private void postProcessBeforeInitialization(ErrorPageRegistry registry) {
    for (ErrorPageRegistrar registrar : getRegistrars()) {
        registrar.registerErrorPages(registry);
    }
}

當創(chuàng)建bean的時候,如果發(fā)現(xiàn)bean實現(xiàn)了ErrorPageRegistry接口,會從Spring容器中查找實現(xiàn)了ErrorPageRegistrar的類,并調(diào)用該類的registerErrorPages方法注冊Error Page;

而在ErrorMvcAutoConfiguration里,提供了ErrorPageRegistrar的實現(xiàn)類ErrorPageCustomizer,默認將錯誤轉發(fā)給/error進行處理;也就是說通過ErrorPageRegistrarBeanPostProcessor會將ErrorPageCustomizer注冊到ErrorPageRegistry;

既然默認情況下將錯誤交給/error進行處理,那我們只要自定義controller類處理/error請求,就可以按需實現(xiàn)自己的錯誤處理;

在ErrorMvcAutoConfiguration類中,提供了BasicErrorController來處理/error的請求,但用戶可以覆蓋實現(xiàn);

接下來看看有哪些類實現(xiàn)了ErrorPageRegistry接口:

public class TomcatEmbeddedServletContainerFactory
    extends AbstractEmbeddedServletContainerFactory{}

public abstract class AbstractEmbeddedServletContainerFactory
    extends AbstractConfigurableEmbeddedServletContainer
    implements EmbeddedServletContainerFactory{}

public abstract class AbstractConfigurableEmbeddedServletContainer
    implements ConfigurableEmbeddedServletContainer{}
    
public interface ConfigurableEmbeddedServletContainer extends ErrorPageRegistry{}

從上面的繼承關系可以看到TomcatEmbeddedServletContainerFactory繞了一大圈,實現(xiàn)了ErrorPageRegistry接口,這意味著創(chuàng)建TomcatEmbeddedServletContainerFactory時,將會注冊ErrorPage;

上面說介紹的,就是利用ErrorPage處理錯誤的相關實現(xiàn);

Spring MVC異常處理

使用Spring Boot框架時,很多時候都是采用Spring MVC處理Http請求;Spring MVC也提供自己異常處理機制:

Spring MVC框架的入口是DispatcherServlet,其中定義了成員變量:

private List<HandlerExceptionResolver> handlerExceptionResolvers;

private void initHandlerExceptionResolvers(ApplicationContext context) {
    this.handlerExceptionResolvers = null;
        //默認為true
    if (this.detectAllHandlerExceptionResolvers) {
        // Find all HandlerExceptionResolvers in the ApplicationContext, including ancestor contexts.
        Map<String, HandlerExceptionResolver> matchingBeans = BeanFactoryUtils
                .beansOfTypeIncludingAncestors(context, HandlerExceptionResolver.class, true, false);
        if (!matchingBeans.isEmpty()) {
            this.handlerExceptionResolvers = new ArrayList<HandlerExceptionResolver>(matchingBeans.values());
            // We keep HandlerExceptionResolvers in sorted order.
            AnnotationAwareOrderComparator.sort(this.handlerExceptionResolvers);
        }
    }
    else {
        try {
            HandlerExceptionResolver her =
                    context.getBean(HANDLER_EXCEPTION_RESOLVER_BEAN_NAME, HandlerExceptionResolver.class);
            this.handlerExceptionResolvers = Collections.singletonList(her);
        }
        catch (NoSuchBeanDefinitionException ex) {
            // Ignore, no HandlerExceptionResolver is fine too.
        }
    }

    // Ensure we have at least some HandlerExceptionResolvers, by registering
    // default HandlerExceptionResolvers if no other resolvers are found.
    if (this.handlerExceptionResolvers == null) {
        this.handlerExceptionResolvers = getDefaultStrategies(context, HandlerExceptionResolver.class);
        if (logger.isDebugEnabled()) {
            logger.debug("No HandlerExceptionResolvers found in servlet '" + getServletName() + "': using default");
        }
    }
}

那么Spring Boot在什么地方提供了HandlerExceptionResolver的實現(xiàn)呢?具體的實現(xiàn)在WebMvcAutoConfiguration類,這里為了方便介紹,將EnableWebMvcConfiguration和它的父類WebMvcConfigurationSupport代碼放到一起:

@Bean
public HandlerExceptionResolver handlerExceptionResolver() {
    List<HandlerExceptionResolver> exceptionResolvers = new ArrayList<HandlerExceptionResolver>();
    configureHandlerExceptionResolvers(exceptionResolvers);
    if (exceptionResolvers.isEmpty()) {
        addDefaultHandlerExceptionResolvers(exceptionResolvers);
    }
    extendHandlerExceptionResolvers(exceptionResolvers);
    HandlerExceptionResolverComposite composite = new HandlerExceptionResolverComposite();
    composite.setOrder(0);
    composite.setExceptionResolvers(exceptionResolvers);
    return composite;
}

@Override
protected void configureHandlerExceptionResolvers(
        List<HandlerExceptionResolver> exceptionResolvers) {
    super.configureHandlerExceptionResolvers(exceptionResolvers);
    if (exceptionResolvers.isEmpty()) {
        addDefaultHandlerExceptionResolvers(exceptionResolvers);
    }
    if (this.mvcProperties.isLogResolvedException()) {
        for (HandlerExceptionResolver resolver : exceptionResolvers) {
            if (resolver instanceof AbstractHandlerExceptionResolver) {
                ((AbstractHandlerExceptionResolver) resolver)
                        .setWarnLogCategory(resolver.getClass().getName());
            }
        }
    }
}

protected final void addDefaultHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) {
    ExceptionHandlerExceptionResolver exceptionHandlerResolver = createExceptionHandlerExceptionResolver();
    exceptionHandlerResolver.setContentNegotiationManager(mvcContentNegotiationManager());
    exceptionHandlerResolver.setMessageConverters(getMessageConverters());
    exceptionHandlerResolver.setCustomArgumentResolvers(getArgumentResolvers());
    exceptionHandlerResolver.setCustomReturnValueHandlers(getReturnValueHandlers());
    if (jackson2Present) {
        exceptionHandlerResolver.setResponseBodyAdvice(
                Collections.<ResponseBodyAdvice<?>>singletonList(new JsonViewResponseBodyAdvice()));
    }
    exceptionHandlerResolver.setApplicationContext(this.applicationContext);
    exceptionHandlerResolver.afterPropertiesSet();
    exceptionResolvers.add(exceptionHandlerResolver);

    ResponseStatusExceptionResolver responseStatusResolver = new ResponseStatusExceptionResolver();
    responseStatusResolver.setMessageSource(this.applicationContext);
    exceptionResolvers.add(responseStatusResolver);

    exceptionResolvers.add(new DefaultHandlerExceptionResolver());
}

從上面的代碼可以看到,最終提供了HandlerExceptionResolverComposite包裝類作為HandlerExceptionResolver的實現(xiàn),包裝了ExceptionHandlerExceptionResolver、ResponseStatusExceptionResolver和
DefaultHandlerExceptionResolver三個具體的實現(xiàn)類;
其中ExceptionHandlerExceptionResolver會讀取方法的@ExceptionHandler注解,從而進行異常處理;

ExceptionHandlerExceptionResolver首先會搜索Controller類本身,接著從添加了ControllerAdvice注解的類里面尋找;

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容