激情久久久_欧美视频区_成人av免费_不卡视频一二三区_欧美精品在欧美一区二区少妇_欧美一区二区三区的

服務(wù)器之家:專注于服務(wù)器技術(shù)及軟件下載分享
分類導(dǎo)航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術(shù)|正則表達(dá)式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務(wù)器之家 - 編程語言 - Java教程 - SpringBoot2.1.4中的錯(cuò)誤處理機(jī)制

SpringBoot2.1.4中的錯(cuò)誤處理機(jī)制

2022-02-19 14:50YO_RUI Java教程

這篇文章主要介紹了SpringBoot2.1.4中的錯(cuò)誤處理機(jī)制,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教

SpringBoot 2.1.4 錯(cuò)誤處理機(jī)制

springboot的自動(dòng)配置中幫我們配置了相關(guān)的錯(cuò)誤處理組件,例如訪問一個(gè)不存在的頁面,就會(huì)出現(xiàn)下面的錯(cuò)誤頁面,上面也會(huì)顯示相應(yīng)的信息

SpringBoot2.1.4中的錯(cuò)誤處理機(jī)制

在Postman軟件中模擬移動(dòng)端訪問,會(huì)獲取如下響應(yīng)的json數(shù)據(jù):

SpringBoot2.1.4中的錯(cuò)誤處理機(jī)制

可以發(fā)現(xiàn)springboot的錯(cuò)誤處理機(jī)制很好的適應(yīng)了不同客戶端訪問,瀏覽器返回頁面,移動(dòng)端返回json,那這背后springboot是如何處理的,顯示的頁面我想自己設(shè)計(jì),或者返回的這些信息我們自己能夠定制嗎?

SpringBoot錯(cuò)誤機(jī)制原理

springboot版本:2.1.4.RELEASE

1、默認(rèn)錯(cuò)誤頁面生成機(jī)制

當(dāng)我們在訪問一個(gè)不存在的路徑時(shí),會(huì)出現(xiàn)上面的錯(cuò)誤頁面,這個(gè)頁面不是我們自己創(chuàng)建的,而是由springboot幫我們生成的,那下面我們首先弄清楚這個(gè)默認(rèn)的錯(cuò)誤頁面(Whitelabel Error Page)是怎么生成的。

1.1 springboot關(guān)于error的自動(dòng)配置

package org.springframework.boot.autoconfigure.web.servlet.error包下有如下的類:

SpringBoot2.1.4中的錯(cuò)誤處理機(jī)制

  • BasicErrorController、AbstractErrorController:錯(cuò)誤請求控制器
  • DefaultErrorViewResolver:錯(cuò)誤視圖解析器
  • ErrorMvcAutoConfiguration:error的自動(dòng)配置類

ErrorMvcAutoConfiguration

在這個(gè)配置類中注冊了一些組件:

@Bean
@ConditionalOnMissingBean(
  value = {ErrorAttributes.class},
  search = SearchStrategy.CURRENT
)
// 關(guān)于error錯(cuò)誤信息的相關(guān)類
public DefaultErrorAttributes errorAttributes() {
  return new DefaultErrorAttributes(this.serverProperties.getError().isIncludeException());
}
@Bean
@ConditionalOnMissingBean(
  value = {ErrorController.class},
  search = SearchStrategy.CURRENT
)
// 處理錯(cuò)誤請求的控制器
public BasicErrorController basicErrorController(ErrorAttributes errorAttributes) {
  return new BasicErrorController(errorAttributes, this.serverProperties.getError(), this.errorViewResolvers);
}
@Bean
// 錯(cuò)誤頁面定制器
public ErrorMvcAutoConfiguration.ErrorPageCustomizer errorPageCustomizer() {
  return new ErrorMvcAutoConfiguration.ErrorPageCustomizer(this.serverProperties, this.dispatcherServletPath);
}

第一步:ErrorPageCustomizer

private static class ErrorPageCustomizer implements ErrorPageRegistrar, Ordered {
      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) {
     		 // getPath()獲取到一個(gè)路徑“/error”
          ErrorPage errorPage = new ErrorPage(this.dispatcherServletPath.getRelativePath(this.properties.getError().getPath())); 
          // 關(guān)鍵點(diǎn):這里講將/error的errorPage注冊到了servlet,在發(fā)生異常時(shí)就會(huì)轉(zhuǎn)發(fā)到/error
          errorPageRegistry.addErrorPages(new ErrorPage[]{errorPage}); 
      }
      public int getOrder() {
          return 0;
      }
  }

注意上面的注釋,這里是為什么發(fā)生錯(cuò)誤就會(huì)發(fā)起/error,很多博客都未說明,當(dāng)然這里沒有討論其內(nèi)部原理。

第二步:BasicErrorController

在錯(cuò)誤發(fā)生后,發(fā)起 “/error” 請求,那這個(gè) “/error” 就會(huì)由上面已經(jīng)注冊的BasicErrorController 接收處理。

@Controller // 表明是個(gè)控制器
@RequestMapping({"${server.error.path:${error.path:/error}}"}) // 映射的路徑:/error
public class BasicErrorController extends AbstractErrorController {
  private final ErrorProperties errorProperties;
  public BasicErrorController(ErrorAttributes errorAttributes, ErrorProperties errorProperties) {
      this(errorAttributes, errorProperties, Collections.emptyList());
  }
  public BasicErrorController(ErrorAttributes errorAttributes, ErrorProperties errorProperties, List<ErrorViewResolver> errorViewResolvers) {
      super(errorAttributes, errorViewResolvers);
      Assert.notNull(errorProperties, "ErrorProperties must not be null");
      this.errorProperties = errorProperties;
  }
  public String getErrorPath() {
      return this.errorProperties.getPath();
  }
	// 處理瀏覽器的請求
  @RequestMapping(
      produces = {"text/html"}
  )
  public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {
      HttpStatus status = this.getStatus(request);
      Map<String, Object> model = Collections.unmodifiableMap(this.getErrorAttributes(request, this.isIncludeStackTrace(request, MediaType.TEXT_HTML)));
      response.setStatus(status.value());
      ModelAndView modelAndView = this.resolveErrorView(request, response, status, model);
      return modelAndView != null ? modelAndView : new ModelAndView("error", model);
  }
	
	// 處理移動(dòng)端的請求
  @RequestMapping
  public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
      Map<String, Object> body = this.getErrorAttributes(request, this.isIncludeStackTrace(request, MediaType.ALL));
      HttpStatus status = this.getStatus(request);
      return new ResponseEntity(body, status);
  }
  protected boolean isIncludeStackTrace(HttpServletRequest request, MediaType produces) {
      IncludeStacktrace include = this.getErrorProperties().getIncludeStacktrace();
      if (include == IncludeStacktrace.ALWAYS) {
          return true;
      } else {
          return include == IncludeStacktrace.ON_TRACE_PARAM ? this.getTraceParameter(request) : false;
      }
  }
  protected ErrorProperties getErrorProperties() {
      return this.errorProperties;
  }
}

這里可以解決一個(gè)疑惑,springboot怎么區(qū)分是瀏覽器還是移動(dòng)端的,主要看這個(gè)方法的注解 produces={“text/html”} ,表示響應(yīng)的數(shù)據(jù)是以html形式返回,這樣當(dāng)瀏覽器訪問時(shí)就會(huì)調(diào)用這個(gè)方法

@RequestMapping(produces = {"text/html"})
public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response){
...

客戶端訪問時(shí)就會(huì)調(diào)用下面的error方法。

@RequestMapping
public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {

下面再來具體分析默認(rèn)錯(cuò)誤頁面如何生成,還是來看到errorHTML方法:

public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {
// 獲取錯(cuò)誤狀態(tài)碼,封裝到HttpStatus里面
      HttpStatus status = this.getStatus(request);
      // 獲取錯(cuò)誤信息,以map形式返回,這個(gè)后面我們具體來看,到底我們能獲取到哪些數(shù)據(jù)
      Map<String, Object> model = Collections.unmodifiableMap(this.getErrorAttributes(request, this.isIncludeStackTrace(request, MediaType.TEXT_HTML)));
      // 設(shè)置響應(yīng)體中狀態(tài)碼
      response.setStatus(status.value());
      // 關(guān)鍵點(diǎn):這里就是在創(chuàng)建視圖對象
      ModelAndView modelAndView = this.resolveErrorView(request, response, status, model);
      return modelAndView != null ? modelAndView : new ModelAndView("error", model);
  }

下面來看這個(gè)resolveErrorView方法,這個(gè)方法是父類AbstractErrorController 中的:

protected ModelAndView resolveErrorView(HttpServletRequest request, HttpServletResponse response, HttpStatus status, Map<String, Object> model) {
// errorViewResolvers是一個(gè)list,存放ErrorViewResolver對象
  Iterator var5 = this.errorViewResolvers.iterator();
  ModelAndView modelAndView;
  // 遍歷集合
  do {
      if (!var5.hasNext()) {
          return null;
      }
      ErrorViewResolver resolver = (ErrorViewResolver)var5.next();
      // 關(guān)鍵點(diǎn):解析器對象進(jìn)行視圖解析
      modelAndView = resolver.resolveErrorView(request, status, model);
  } while(modelAndView == null);
  return modelAndView;
}

這里的resolveErrorView方法屬于DefaultErrorViewResolver:

public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus status, Map<String, Object> model) {
 // 調(diào)用下面的方法解析視圖,傳入?yún)?shù)為錯(cuò)誤狀態(tài)碼,錯(cuò)誤信息的map
      ModelAndView modelAndView = this.resolve(String.valueOf(status.value()), model);
      if (modelAndView == null && SERIES_VIEWS.containsKey(status.series())) {
          modelAndView = this.resolve((String)SERIES_VIEWS.get(status.series()), model);
      }
      return modelAndView;
  }
  private ModelAndView resolve(String viewName, Map<String, Object> model) {
   // 定義視圖名,這里我們可以確定視圖名:error/錯(cuò)誤碼,例如:error/404,
      String errorViewName = "error/" + viewName;
      // 這里結(jié)合上面的errorViewName,其實(shí)就是在template目錄下的error目錄進(jìn)行查找
      // 我們默認(rèn)情況下是沒有error目錄,這里的provide最終值為null,代碼較多就不一一展示,有興趣的可以跟下去
      TemplateAvailabilityProvider provider = this.templateAvailabilityProviders.getProvider(errorViewName, this.applicationContext);
      // 根據(jù)判定,這里會(huì)接著調(diào)用下面的resolveResource方法
      return provider != null ? new ModelAndView(errorViewName, model) : this.resolveResource(errorViewName, model);
  }
  private ModelAndView resolveResource(String viewName, Map<String, Object> model) {
   //  getStaticLocations()獲取的是靜態(tài)資源路徑:"classpath:/META-INF/resources/", "classpath:/resources/","classpath:/static/", "classpath:/public/"
      String[] var3 = this.resourceProperties.getStaticLocations();
      int var4 = var3.length;
// 遍歷上面的4個(gè)靜態(tài)資源路徑
      for(int var5 = 0; var5 < var4; ++var5) {
          String location = var3[var5];
          try {
              Resource resource = this.applicationContext.getResource(location);
              // 創(chuàng)建resource對象,例如error/404.html
              resource = resource.createRelative(viewName + ".html");
              // 查找在對應(yīng)靜態(tài)資源目錄下是否有上面的這個(gè)資源對象,有就創(chuàng)建視圖對象
              if (resource.exists()) {
                  return new ModelAndView(new DefaultErrorViewResolver.HtmlResourceView(resource), model);
              }
          } catch (Exception var8) {
              ;
          }
      }
// 都沒找到就返回null,默認(rèn)情況下是不存在error目錄的,所以這里最終返回null
      return null;
  }

當(dāng)resolveResource方法執(zhí)行完返回null,resolve方法也就返回null,在回到resolveErrorView

public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus status, Map<String, Object> model) {
 // 調(diào)用下面的方法解析視圖,傳入?yún)?shù)為錯(cuò)誤狀態(tài)碼,錯(cuò)誤信息的map
      ModelAndView modelAndView = this.resolve(String.valueOf(status.value()), model);
      // 上面分析得到modelAndView的值為null,下面的if中SERIES_VIEWS.containsKey(status.series())是在判斷錯(cuò)誤碼的首位是否為1,2,3,4,5,這個(gè)大家下去可以跟一下
      if (modelAndView == null && SERIES_VIEWS.containsKey(status.series())) {
          modelAndView = this.resolve((String)SERIES_VIEWS.get(status.series()), model);
      }
// if里面的resolve方法分析跟上面一樣,默認(rèn)情況下是沒有4xx.html/5xx.html頁面文件的,所以最終這里返回null
      return modelAndView;
  }

這個(gè)resolveErrorView方法執(zhí)行完后,我們就可以回到最開始處理 “/error” 請求的errorHtml方法了

@RequestMapping(produces = {"text/html"} )
public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {
  HttpStatus status = this.getStatus(request);
  Map<String, Object> model = Collections.unmodifiableMap(this.getErrorAttributes(request, this.isIncludeStackTrace(request, MediaType.TEXT_HTML)));
  response.setStatus(status.value());
  ModelAndView modelAndView = this.resolveErrorView(request, response, status, model);
  // modelAnView根據(jù)上面的分析其值為null
  return modelAndView != null ? modelAndView : new ModelAndView("error", model);
}

當(dāng)modelAndView為null時(shí),將會(huì)執(zhí)行'new ModelAndView(“error”, model),那這個(gè)“error”又是什么呢?看下面WhitelabelErrorViewConfiguration 里面有個(gè)組件其 name就是error,這個(gè)組件是StaticView,就是一個(gè)View,里面的視圖渲染方法render中的內(nèi)容就是最開始我們看到的那個(gè)錯(cuò)誤頁面的內(nèi)容。

@Conditional({ErrorMvcAutoConfiguration.ErrorTemplateMissingCondition.class})
protected static class WhitelabelErrorViewConfiguration {
  private final ErrorMvcAutoConfiguration.StaticView defaultErrorView = new ErrorMvcAutoConfiguration.StaticView();
  protected WhitelabelErrorViewConfiguration() {
  }
  @Bean(name = {"error"})
  @ConditionalOnMissingBean(name = {"error"})
  public View defaultErrorView() {
      return this.defaultErrorView;
  }
...
}
private static class StaticView implements View {
  private static final Log logger = LogFactory.getLog(ErrorMvcAutoConfiguration.StaticView.class);
  private StaticView() {
  }
  public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
      if (response.isCommitted()) {
          String message = this.getMessage(model);
          logger.error(message);
      } else {
          StringBuilder builder = new StringBuilder();
          Date timestamp = (Date)model.get("timestamp");
          Object message = model.get("message");
          Object trace = model.get("trace");
          if (response.getContentType() == null) {
              response.setContentType(this.getContentType());
          }
          builder.append("<html><body><h1>Whitelabel Error Page</h1>").append("<p>This application has no explicit mapping for /error, so you are seeing this as a fallback.</p>").append("<div id='created'>").append(timestamp).append("</div>").append("<div>There was an unexpected error (type=").append(this.htmlEscape(model.get("error"))).append(", status=").append(this.htmlEscape(model.get("status"))).append(").</div>");
          if (message != null) {
              builder.append("<div>").append(this.htmlEscape(message)).append("</div>");
          }
          if (trace != null) {
              builder.append("<div style='white-space:pre-wrap;'>").append(this.htmlEscape(trace)).append("</div>");
          }
          builder.append("</body></html>");
          response.getWriter().append(builder.toString());
      }
  }
  private String htmlEscape(Object input) {
      return input != null ? HtmlUtils.htmlEscape(input.toString()) : null;
  }
  private String getMessage(Map<String, ?> model) {
      Object path = model.get("path");
      String message = "Cannot render error page for request [" + path + "]";
      if (model.get("message") != null) {
          message = message + " and exception [" + model.get("message") + "]";
      }
      message = message + " as the response has already been committed.";
      message = message + " As a result, the response may have the wrong status code.";
      return message;
  }
  public String getContentType() {
      return "text/html";
  }
}

所以,整個(gè)大致的過程到此結(jié)束了,默認(rèn)情況下錯(cuò)誤請求處理完成后就返回的這個(gè)StaticView定義的頁面,下圖做個(gè)基本的梳理。后續(xù)再來做自定義錯(cuò)誤頁面、自定義錯(cuò)誤數(shù)據(jù)的原理分析。

SpringBoot2.1.4中的錯(cuò)誤處理機(jī)制

 

SpringBoot 2.1.3 錯(cuò)誤處理機(jī)制

引用的問題做個(gè)標(biāo)記

以前的引用好像在新版本中無法引用了

錯(cuò)誤處理機(jī)制

其他的程序的類的聲明直接用IDEA的提示來用就可以了。

如果還是有錯(cuò)誤的話,就進(jìn)入到lib中看看引用的類的方法就可以了

import org.springframework.boot.autoconfigration.web.DefaultErrorAttributes;//這是以前的
import org.springframework.boot.web.servlet.error.DefaultErrorAttributes;//這是現(xiàn)在的

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持服務(wù)器之家。

原文鏈接:https://blog.csdn.net/qq_34975710/article/details/89892048

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 久久久久久久久久综合 | 日本看片一区二区三区高清 | 国产无限资源在线观看 | 午夜视频观看 | 国产精品久久久久久久久久 | 亚洲成人久久精品 | 久久久国产视频 | 黄网站免费观看视频 | 成人在线视频播放 | 成人h精品动漫一区二区三区 | 日朝毛片 | 亚洲精品欧美一区二区三区 | 一级毛片免费高清视频 | h视频免费看 | 国产高清美女一级毛片 | 欧美亚洲一区二区三区四区 | 国产一级性生活视频 | 国产精品自拍av | 国产精品一区二区免费在线观看 | 午夜视频在线免费 | 欧美性生话视频 | 欧美成人黄色小视频 | 黑人一级片视频 | 蜜桃传媒视频麻豆第一区免费观看 | 禁漫天堂久久久久久久久久 | 国产91在线亚洲 | 高清视频一区二区 | 美女毛片在线观看 | 成人久久久久久久久久 | 91九色精品国产 | 精品国内视频 | 韩国三级日本三级香港三级黄 | 日韩一级片一区二区三区 | 免费黄色入口 | 国产做爰 | 斗罗破苍穹在线观看免费完整观看 | 香蕉黄色网 | 久久蜜桃精品一区二区三区综合网 | 在线影院av| 国产区二区 | 深夜小视频在线观看 |