自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

SpringBoot錯(cuò)誤頁面的原理,你知道嗎?

開發(fā) 前端
通過分析,默認(rèn)情況下springboot容器啟動(dòng)后會(huì)像tomcat容器中注冊(cè)一個(gè)/error的錯(cuò)誤頁,這個(gè)/error又是誰呢?

環(huán)境:Springboot3.0.5

錯(cuò)誤消息格式

有如下接口:

@RestController
@RequestMapping("/demo")
public class DemoController {


  @GetMapping("/index")
  public Object index() {
    System.out.println(1 / 0) ;
    return "/demo/index" ;
  }
  
}

當(dāng)訪問上面接口后,默認(rèn)情況下Springboot會(huì)返回如下錯(cuò)誤信息:

圖片

當(dāng)請(qǐng)求的Accept是text/html返回的是HTML結(jié)果,當(dāng)Accpet是application/json返回如下:

圖片

后臺(tái)接口會(huì)根據(jù)不同的Accept返回不同的數(shù)據(jù)格式。

錯(cuò)誤處理原理

Springboot在啟動(dòng)過程中會(huì)執(zhí)行如下處理:

public abstract class AbstractApplicationContext {
  public void refresh() {
    onRefresh();
  }
}

ServletWebServerApplicationContext

public class ServletWebServerApplicationContext {
  protected void onRefresh() {
    super.onRefresh();
    try {
      // 創(chuàng)建web服務(wù)
      createWebServer();
    } catch (Throwable ex) {
      throw new ApplicationContextException("Unable to start web server", ex);
    }
  }
  private void createWebServer() {
    // 這里假設(shè)我們使用的是Tomcat容器,那么這里的factory = TomcatServletWebServerFactory
    this.webServer = factory.getWebServer(getSelfInitializer());
  }
}

TomcatServletWebServerFactory

public class TomcatServletWebServerFactory {
  public WebServer getWebServer(ServletContextInitializer... initializers) {
    // 創(chuàng)建Tomcat實(shí)例
    Tomcat tomcat = new Tomcat();
    // ...
    // 準(zhǔn)備上下文
    prepareContext(tomcat.getHost(), initializers);
    return getTomcatWebServer(tomcat);
  }
  protected void prepareContext(Host host, ServletContextInitializer[] initializers) {
    // 該類繼承自StandardContext類(該類所屬tomcat,每一個(gè)StandardContext代表了一個(gè)webapp)
    TomcatEmbeddedContext context = new TomcatEmbeddedContext();
    // ...
    // 配置上下文
    configureContext(context, initializersToUse);
  }
  // 配置上下文
  protected void configureContext(Context context, ServletContextInitializer[] initializers) {
    // 獲取當(dāng)前Spring容器中配置的ErrorPage,然后注冊(cè)到Tomcat當(dāng)前webapp的上下文中
    // 這里其實(shí)對(duì)應(yīng)的就是web.xml中配置的錯(cuò)誤頁
    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類實(shí)現(xiàn)了ErrorPageRegistry接口,有如下方法:

// TomcatServletWebServerFactory繼承自AbstractConfigurableWebServerFactory
// ConfigurableWebServerFactory接口繼承了ErrorPageRegistry接口
public abstract class AbstractConfigurableWebServerFactory implements ConfigurableWebServerFactory {
  public void addErrorPages(ErrorPage... errorPages) {
    this.errorPages.addAll(Arrays.asList(errorPages));
  }
}

下面查看上面的addErrorPages方法是如何被調(diào)用的。

在自動(dòng)配置類中導(dǎo)入了下面的類

@Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class})
public class ServletWebServerFactoryAutoConfiguration {
}
// BeanPostProcessorsRegistrar實(shí)現(xiàn)了ImportBeanDefinitionRegistrar,用來注冊(cè)Bean
public static class BeanPostProcessorsRegistrar implements ImportBeanDefinitionRegistrar, BeanFactoryAware {
  public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
    // 注冊(cè)了BeanPostProcessor類ErrorPageRegistrarBeanPostProcessor
    registerSyntheticBeanIfMissing(registry, "errorPageRegistrarBeanPostProcessor", ErrorPageRegistrarBeanPostProcessor.class);
  }
}
public class ErrorPageRegistrarBeanPostProcessor implements BeanPostProcessor, BeanFactoryAware {
  public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
    // 判斷當(dāng)前的Bean是否實(shí)現(xiàn)了ErrorPageRegistry
    if (bean instanceof ErrorPageRegistry errorPageRegistry) {
      postProcessBeforeInitialization(errorPageRegistry);
    }
    return bean;
  }
  private void postProcessBeforeInitialization(ErrorPageRegistry registry) {
    // 遍歷所有的ErrorPageRegistrar,注冊(cè)到當(dāng)前實(shí)現(xiàn)了ErrorPageRegistry接口的Bean中
    // 如上面的TomcatServletWebServerFactory
    for (ErrorPageRegistrar registrar : getRegistrars()) {
      registrar.registerErrorPages(registry);
    }
  }
  private Collection<ErrorPageRegistrar> getRegistrars() {
    if (this.registrars == null) {
      // 獲取容器中所有的ErrorPageRegistrar Bean對(duì)象
      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,然后注冊(cè)到ErrorPageRegistry中。

在如下自動(dòng)配置類中定義了一個(gè)默認(rèn)的錯(cuò)誤頁對(duì)象。

public class ErrorMvcAutoConfiguration {
  @Bean
  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;
    }


    @Override
    public void registerErrorPages(ErrorPageRegistry errorPageRegistry) {
      // 獲取server.error.path配置屬性
      ErrorPage errorPage = new ErrorPage(this.dispatcherServletPath.getRelativePath(this.properties.getError().getPath()));
      errorPageRegistry.addErrorPages(errorPage);
    }
  }
}
@ConfigurationProperties(prefix = "server", ignoreUnknownFields = true)
public class ServerProperties {
  @NestedConfigurationProperty
  private final ErrorProperties error = new ErrorProperties();
}
public class ErrorProperties {
  // 讀取error.path配置,如果沒有配置,則默認(rèn)是/error
  @Value("${error.path:/error}")
  private String path = "/error";
}

通過上面的分析,默認(rèn)情況下springboot容器啟動(dòng)后會(huì)像tomcat容器中注冊(cè)一個(gè)/error的錯(cuò)誤頁,這個(gè)/error又是誰呢?

默認(rèn)錯(cuò)誤Controller

在默認(rèn)的錯(cuò)誤頁自動(dòng)配置中:

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


  // 該Controller就是默認(rèn)的/error錯(cuò)誤跳轉(zhuǎn)的類
  @Bean
  @ConditionalOnMissingBean(value = ErrorController.class, search = SearchStrategy.CURRENT)
  public BasicErrorController basicErrorController(ErrorAttributes errorAttributes, ObjectProvider<ErrorViewResolver> errorViewResolvers) {
    return new BasicErrorController(errorAttributes, this.serverProperties.getError(),errorViewResolvers.orderedStream().toList());
  }
}
@Controller
@RequestMapping("${server.error.path:${error.path:/error}}")
public class BasicErrorController extends AbstractErrorController {
  // 當(dāng)請(qǐng)求的Accept=text/html時(shí)調(diào)用該方法
  @RequestMapping(produces = MediaType.TEXT_HTML_VALUE)
  public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {
    // ...
  }
  // 當(dāng)請(qǐng)求的Accept=application/json時(shí)調(diào)用該方法
  @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, getErrorAttributeOptions(request, MediaType.ALL));
    return new ResponseEntity<>(body, status);
  }
}

以上就是當(dāng)springboot默認(rèn)情況下發(fā)生錯(cuò)誤時(shí)的執(zhí)行輸出原理。

責(zé)任編輯:武曉燕 來源: 實(shí)戰(zhàn)案例錦集
相關(guān)推薦

2024-02-19 00:00:00

Console函數(shù)鏈接庫

2024-02-26 08:19:00

WebSpring容器

2024-05-28 09:12:10

2024-04-07 00:00:00

ESlint命令變量

2023-12-12 08:41:01

2023-12-20 08:23:53

NIO組件非阻塞

2024-04-30 09:02:48

2023-04-26 10:21:04

2019-09-24 21:00:59

SQL數(shù)據(jù)庫基礎(chǔ)數(shù)據(jù)庫

2021-10-14 06:52:47

算法校驗(yàn)碼結(jié)構(gòu)

2022-11-04 14:16:05

2024-09-18 07:00:00

消息隊(duì)列中間件消息隊(duì)列

2025-02-18 08:11:17

2023-03-21 07:39:51

CentOS掛載硬盤

2022-12-02 14:12:52

新能源汽車海爾

2023-01-13 17:02:10

操作系統(tǒng)鴻蒙

2020-02-20 08:30:49

OSPF網(wǎng)絡(luò)協(xié)議路由協(xié)議

2022-11-28 00:04:17

2024-07-08 00:00:01

多線程ThreadC#

2022-09-29 15:32:58

云計(jì)算計(jì)算模式
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)