最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

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

 更新時(shí)間:2021年10月14日 09:22:48   作者:YO_RUI  
這篇文章主要介紹了SpringBoot2.1.4中的錯(cuò)誤處理機(jī)制,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教

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

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

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

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

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

springboot版本:2.1.4.RELEASE

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

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

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

package org.springframework.boot.autoconfigure.web.servlet.error包下有如下的類(lèi):

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

ErrorMvcAutoConfiguration

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

@Bean
@ConditionalOnMissingBean(
    value = {ErrorAttributes.class},
    search = SearchStrategy.CURRENT
)
// 關(guān)于error錯(cuò)誤信息的相關(guān)類(lèi)
public DefaultErrorAttributes errorAttributes() {
    return new DefaultErrorAttributes(this.serverProperties.getError().isIncludeException());
}
@Bean
@ConditionalOnMissingBean(
    value = {ErrorController.class},
    search = SearchStrategy.CURRENT
)
// 處理錯(cuò)誤請(qǐng)求的控制器
public BasicErrorController basicErrorController(ErrorAttributes errorAttributes) {
    return new BasicErrorController(errorAttributes, this.serverProperties.getError(), this.errorViewResolvers);
}
@Bean
// 錯(cuò)誤頁(yè)面定制器
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注冊(cè)到了servlet,在發(fā)生異常時(shí)就會(huì)轉(zhuǎn)發(fā)到/error
            errorPageRegistry.addErrorPages(new ErrorPage[]{errorPage}); 
        }
        public int getOrder() {
            return 0;
        }
    }

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

第二步:BasicErrorController

在錯(cuò)誤發(fā)生后,發(fā)起 “/error” 請(qǐng)求,那這個(gè) “/error” 就會(huì)由上面已經(jīng)注冊(cè)的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();
    }
	// 處理瀏覽器的請(qǐng)求
    @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)端的請(qǐ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)瀏覽器訪(fǎng)問(wèn)時(shí)就會(huì)調(diào)用這個(gè)方法

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

客戶(hù)端訪(fǎng)問(wèn)時(shí)就會(huì)調(diào)用下面的error方法。

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

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

public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {
  // 獲取錯(cuò)誤狀態(tài)碼,封裝到HttpStatus里面
        HttpStatus status = this.getStatus(request);
        // 獲取錯(cuò)誤信息,以map形式返回,這個(gè)后面我們具體來(lái)看,到底我們能獲取到哪些數(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)建視圖對(duì)象
        ModelAndView modelAndView = this.resolveErrorView(request, response, status, model);
        return modelAndView != null ? modelAndView : new ModelAndView("error", model);
    }

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

protected ModelAndView resolveErrorView(HttpServletRequest request, HttpServletResponse response, HttpStatus status, Map<String, Object> model) {
 // errorViewResolvers是一個(gè)list,存放ErrorViewResolver對(duì)象
    Iterator var5 = this.errorViewResolvers.iterator();
    ModelAndView modelAndView;
    // 遍歷集合
    do {
        if (!var5.hasNext()) {
            return null;
        }
        ErrorViewResolver resolver = (ErrorViewResolver)var5.next();
        // 關(guān)鍵點(diǎn):解析器對(duì)象進(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)情況下是沒(méi)有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對(duì)象,例如error/404.html
                resource = resource.createRelative(viewName + ".html");
                // 查找在對(duì)應(yīng)靜態(tài)資源目錄下是否有上面的這個(gè)資源對(duì)象,有就創(chuàng)建視圖對(duì)象
                if (resource.exists()) {
                    return new ModelAndView(new DefaultErrorViewResolver.HtmlResourceView(resource), model);
                }
            } catch (Exception var8) {
                ;
            }
        }
  // 都沒(méi)找到就返回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)情況下是沒(méi)有4xx.html/5xx.html頁(yè)面文件的,所以最終這里返回null
        return modelAndView;
    }

這個(gè)resolveErrorView方法執(zhí)行完后,我們就可以回到最開(kāi)始處理 “/error” 請(qǐng)求的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)容就是最開(kāi)始我們看到的那個(gè)錯(cuò)誤頁(yè)面的內(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è)大致的過(guò)程到此結(jié)束了,默認(rèn)情況下錯(cuò)誤請(qǐng)求處理完成后就返回的這個(gè)StaticView定義的頁(yè)面,下圖做個(gè)基本的梳理。后續(xù)再來(lái)做自定義錯(cuò)誤頁(yè)面、自定義錯(cuò)誤數(shù)據(jù)的原理分析。

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

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

以前的引用好像在新版本中無(wú)法引用了

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

其他的程序的類(lèi)的聲明直接用IDEA的提示來(lái)用就可以了。

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

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

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

相關(guān)文章

  • 使用IDEA如何打包發(fā)布SpringBoot并部署到云服務(wù)器

    使用IDEA如何打包發(fā)布SpringBoot并部署到云服務(wù)器

    這篇文章主要介紹了使用IDEA如何打包發(fā)布SpringBoot并部署到云服務(wù)器問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-12-12
  • jvm細(xì)節(jié)探索之synchronized及實(shí)現(xiàn)問(wèn)題分析

    jvm細(xì)節(jié)探索之synchronized及實(shí)現(xiàn)問(wèn)題分析

    這篇文章主要介紹了jvm細(xì)節(jié)探索之synchronized及實(shí)現(xiàn)問(wèn)題分析,涉及synchronized的字節(jié)碼表示,JVM中鎖的優(yōu)化,對(duì)象頭的介紹等相關(guān)內(nèi)容,具有一定借鑒價(jià)值,需要的朋友可以參考下。
    2017-11-11
  • 2024最新版Java?JDK安裝配置圖文詳解全攻略

    2024最新版Java?JDK安裝配置圖文詳解全攻略

    這篇文章主要介紹了2024最新版Java?JDK安裝配置圖文詳解的相關(guān)資料,包含準(zhǔn)備工作、下載步驟、安裝指南及環(huán)境變量配置驗(yàn)證,供用戶(hù)輕松搭建Java開(kāi)發(fā)環(huán)境,需要的朋友可以參考下
    2024-09-09
  • Map與JavaBean相互轉(zhuǎn)換的工具類(lèi)?

    Map與JavaBean相互轉(zhuǎn)換的工具類(lèi)?

    這篇文章主要介紹了Map與JavaBean相互轉(zhuǎn)換的工具類(lèi),在做導(dǎo)入的時(shí)候,遇到了需要將map對(duì)象轉(zhuǎn)化?成javabean的問(wèn)題,也就是說(shuō),不清楚javabean的內(nèi)部字段排列,只知道m(xù)ap的?key代表javabean的字段名,value代表值,需要的朋友可以參考下
    2022-02-02
  • JAVA加密算法數(shù)字簽名實(shí)現(xiàn)原理詳解

    JAVA加密算法數(shù)字簽名實(shí)現(xiàn)原理詳解

    這篇文章主要介紹了JAVA加密算法數(shù)字簽名實(shí)現(xiàn)原理詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-10-10
  • springBoot?之spring.factories擴(kuò)展機(jī)制示例解析

    springBoot?之spring.factories擴(kuò)展機(jī)制示例解析

    這篇文章主要為大家介紹了springBoot?之spring.factories擴(kuò)展機(jī)制示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-04-04
  • 關(guān)于Java?CPU或內(nèi)存使用率過(guò)高問(wèn)題定位

    關(guān)于Java?CPU或內(nèi)存使用率過(guò)高問(wèn)題定位

    Spring?cloud微服務(wù)廣泛應(yīng)用后,服務(wù)的監(jiān)控和運(yùn)維壓力也與日俱增,經(jīng)常有服務(wù)出現(xiàn)CPU或者內(nèi)存使用率過(guò)高的告警,那么遇到這樣的問(wèn)題我們?cè)撊绾闻挪槟??我們可以借助哪些工具?lái)定位問(wèn)題呢?本文將介紹一下遇到此類(lèi)問(wèn)題的解決思路和方法
    2024-10-10
  • 解析Java的Jackson庫(kù)中Streaming API的使用

    解析Java的Jackson庫(kù)中Streaming API的使用

    這篇文章主要介紹了解析Java的Jackson庫(kù)中Streaming API的使用,Jackson被用于Java對(duì)象和JSON的互相轉(zhuǎn)換,需要的朋友可以參考下
    2016-01-01
  • java設(shè)計(jì)模式之適配器模式

    java設(shè)計(jì)模式之適配器模式

    這篇文章主要為大家詳細(xì)介紹了java設(shè)計(jì)模式之適配器模式,介紹了什么是適配器模式,適配器模式的種類(lèi),感興趣的小伙伴們可以參考一下
    2016-08-08
  • IDEA搭建多模塊的Maven項(xiàng)目方式(相互依賴(lài))

    IDEA搭建多模塊的Maven項(xiàng)目方式(相互依賴(lài))

    這篇文章主要介紹了IDEA搭建多模塊的Maven項(xiàng)目方式(相互依賴(lài)),具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-08-08

最新評(píng)論

德阳市| 柳河县| 武穴市| 福海县| 祁东县| 涿州市| 宁远县| 内江市| 滁州市| 南丹县| 彭水| 仁化县| 高雄市| 枞阳县| 石城县| 栾川县| 钟祥市| 三原县| 基隆市| 习水县| 峨边| 山东| 南通市| 句容市| 方山县| 富平县| 宜君县| 红桥区| 兴业县| 繁昌县| 龙南县| 石嘴山市| 青川县| 凤庆县| 松江区| 赤峰市| 翁源县| 龙胜| 交口县| 绥滨县| 宁南县|