日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

bean加载时调用@value时会出现空指针异常_SpringMVC全局异常处理机制

發(fā)布時(shí)間:2025/4/5 javascript 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 bean加载时调用@value时会出现空指针异常_SpringMVC全局异常处理机制 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

SpringMVC全局異常處理

SpringMVC除了可以做URL映射和請(qǐng)求攔截外,還可以做全局異常的處理。全局異常處理可能我們平時(shí)比較少機(jī)會(huì)接觸,但是每個(gè)項(xiàng)目都肯定會(huì)做這個(gè)處理。比如在上一間公司,是前后端分離的架構(gòu),所以后端只要有運(yùn)行時(shí)異常就會(huì)報(bào)“系統(tǒng)異常,請(qǐng)稍后再試”。如果想要走上架構(gòu)師的話,這個(gè)肯定是要學(xué)會(huì)的。

SpringMVC全局異常處理機(jī)制

首先,要知道全局異常處理,SpringMVC提供了兩種方式:

  • 實(shí)現(xiàn)HandlerExceptionResolver接口,自定義異常處理器。
  • 使用HandlerExceptionResolver接口的子類,也就是SpringMVC提供的異常處理器。

第一種是自定義異常處理器,第二種是SpringMVC提供的。接下來先說SpringMVC提供的幾種異常處理器的使用方式,然后再講自定義異常處理器。

SpringMVC提供的異常處理器有哪些呢?我們可以直接看源碼的類圖。

可以看出有四種:

  • DefaultHandlerExceptionResolver,默認(rèn)的異常處理器。根據(jù)各個(gè)不同類型的異常,返回不同的異常視圖。
  • SimpleMappingExceptionResolver,簡單映射異常處理器。通過配置異常類和view的關(guān)系來解析異常。
  • ResponseStatusExceptionResolver,狀態(tài)碼異常處理器。解析帶有@ResponseStatus注釋類型的異常。
  • ExceptionHandlerExceptionResolver,注解形式的異常處理器。對(duì)@ExceptionHandler注解的方法進(jìn)行異常解析。

DefaultHandlerExceptionResolver

這個(gè)異常處理器是SprngMVC默認(rèn)的一個(gè)處理器,處理一些常見的異常,比如:沒有找到請(qǐng)求參數(shù),參數(shù)類型轉(zhuǎn)換異常,請(qǐng)求方式不支持等等。

DefaultHandlerExceptionResolver類的doResolveException()方法:

@Override@Nullableprotected ModelAndView doResolveException(HttpServletRequest request, HttpServletResponse response,@Nullable Object handler, Exception ex) {try {if (ex instanceof HttpRequestMethodNotSupportedException) {return handleHttpRequestMethodNotSupported((HttpRequestMethodNotSupportedException) ex, request,response, handler);}else if (ex instanceof HttpMediaTypeNotSupportedException) {return handleHttpMediaTypeNotSupported((HttpMediaTypeNotSupportedException) ex, request, response,handler);}else if (ex instanceof HttpMediaTypeNotAcceptableException) {return handleHttpMediaTypeNotAcceptable((HttpMediaTypeNotAcceptableException) ex, request, response,handler);}//省略...以下還有十幾種異常的else-if}catch (Exception handlerException) {//是否打開日志,如果打開,那就記錄日志if (logger.isWarnEnabled()) {logger.warn("Handling of [" + ex.getClass().getName() + "] resulted in Exception", handlerException);}}return null;}

通過if-else判斷,判斷繼承什么異常就顯示對(duì)應(yīng)的錯(cuò)誤碼和錯(cuò)誤提示信息。由此可以知道,處理一般有兩步,一是設(shè)置響應(yīng)碼,二是在響應(yīng)頭設(shè)置異常信息。

MissingServletRequestPartException的處理的源碼:

protected ModelAndView handleMissingServletRequestPartException(MissingServletRequestPartException ex,HttpServletRequest request, HttpServletResponse response, @Nullable Object handler) throws IOException {//設(shè)置響應(yīng)碼,設(shè)置異常信息,SC_BAD_REQUEST就是400(bad request)response.sendError(HttpServletResponse.SC_BAD_REQUEST, ex.getMessage());return new ModelAndView();}//響應(yīng)碼public static final int SC_BAD_REQUEST = 400;

為什么要存在這個(gè)異常處理器呢?

從框架的設(shè)計(jì)理念來看,這種公共的、常見的異常應(yīng)該交給框架本身來完成,是一些必需處理的異常。比如參數(shù)類型轉(zhuǎn)換異常,如果程序員不處理,還有框架提供默認(rèn)的處理方式,不至于出現(xiàn)這種錯(cuò)誤而無法排查。

SimpleMappingExceptionResolver

這種異常處理器需要提前配置異常類和對(duì)應(yīng)的view視圖。一般用于使用JSP的項(xiàng)目中,出現(xiàn)異常則通過這個(gè)異常處理器跳轉(zhuǎn)到指定的頁面。

怎么配置?首先搭建JSP項(xiàng)目我就不浪費(fèi)篇幅介紹了。首先要加載一個(gè)XML文件。

@SpringBootApplication //在啟動(dòng)類,加載配置文件 @ImportResource("classpath:spring-config.xml") public class Application {public static void main(String[] args) {SpringApplication.run(Application.class, args);} }

然后在resources目錄下,創(chuàng)建一個(gè)spring-config.xml文件,內(nèi)容如下:

<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"><bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver"><!-- 定義默認(rèn)的異常處理頁面 --><property name="defaultErrorView" value="err"/><!-- 定義異常處理頁面用來獲取異常信息的變量名,默認(rèn)名為exception --><property name="exceptionAttribute" value="ex"/><!-- 定義需要特殊處理的異常,用類名或完全路徑名作為key,異常也頁名作為值 --><property name="exceptionMappings"><props><!-- 數(shù)組越界異常 --><prop key="java.lang.ArrayIndexOutOfBoundsException">err/arrayIndexOutOfBounds</prop><!-- 空指針異常 --><prop key="java.lang.NullPointerException">err/nullPointer</prop></props></property></bean> </beans>

然后在webapp也就是存放JSP頁面的目錄下,創(chuàng)建兩個(gè)JSP頁面。

arrayIndexOutOfBounds.jsp如下:

<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head><title>數(shù)組越界異常</title> </head> <body> <h1>數(shù)組越界異常</h1> <br> <%-- 打印異常到頁面上 --%> <% Exception ex = (Exception)request.getAttribute("ex"); %> <br> <div><%= ex.getMessage() %></div> <% ex.printStackTrace(new java.io.PrintWriter(out)); %> </body> </html>

nullPointer.jsp如下:

<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head><title>空指針異常</title> </head> <body> <h1>空指針異常</h1> <br> <%-- 打印異常到頁面上 --%> <% Exception ex = (Exception)request.getAttribute("ex"); %> <br> <div><%=ex.getMessage()%></div> <% ex.printStackTrace(new java.io.PrintWriter(out)); %> </body> </html>

接著創(chuàng)建兩個(gè)Controller,分別拋出空指針異常和數(shù)組越界異常。

@Controller @RequestMapping("/error") public class ErrController {@RequestMapping("/null")public String err() throws Exception{String str = null;//拋出空指針異常int length = str.length();System.out.println(length);return "index";}@RequestMapping("/indexOut")public String indexOut() throws Exception{int[] nums = new int[2];for (int i = 0; i < 3; i++) {//拋出數(shù)組越界異常nums[i] = i;System.out.println(nums[i]);}return "index";} }

啟動(dòng)項(xiàng)目后,我們發(fā)送兩個(gè)請(qǐng)求,就可以看到:

通過上述例子可以看出,其實(shí)對(duì)于現(xiàn)在前后端分離的項(xiàng)目來說,這種異常處理器已經(jīng)不是很常用了。

ResponseStatusExceptionResolver

這種異常處理器主要用于處理帶有@ResponseStatus注釋的異常。下面演示一下使用方式。

首先自定義異常類繼承Exception,并且使用@ResponseStatus注解修飾。如下:

//value需要使用HttpStatus枚舉類型,HttpStatus.FORBIDDEN=403。 @ResponseStatus(value = HttpStatus.FORBIDDEN,reason = "My defined Exception") public class DefinedException extends Exception{ }

然后再在Controller層拋出此異常。如下:

@Controller @RequestMapping("/error") public class ErrController {@RequestMapping("/myException")public String ex(@RequestParam(name = "num") Integer num) throws Exception {if (num == 1) {//拋出自定義異常throw new DefinedException();}return "index";} }

然后啟動(dòng)項(xiàng)目,請(qǐng)求接口,可以看到如下信息:

使用這種異常處理器,需要自定義一個(gè)異常,一定要一直往上層拋出異常,如果不往上層拋出,在service或者dao層就try-catch處理掉的話,是不會(huì)觸發(fā)的。

ExceptionHandlerExceptionResolver

這個(gè)異常處理器才是最重要的,也是最常用,最靈活的,因?yàn)槭鞘褂米⒔狻J紫任覀冞€是簡單地演示一下怎么使用:

首先需要定義一個(gè)全局的異常處理器。

//這里使用了RestControllerAdvice,是@ResponseBody和@ControllerAdvice的結(jié)合 //會(huì)把實(shí)體類轉(zhuǎn)成JSON格式的提示返回,符合前后端分離的架構(gòu) @RestControllerAdvice public class GlobalExceptionHandler {//這里自定義了一個(gè)BaseException,當(dāng)拋出BaseException異常就會(huì)被此方法處理@ExceptionHandler(BaseException.class)public ErrorInfo errorHandler(HttpServletRequest req, BaseException e) throws Exception {ErrorInfo r = new ErrorInfo();r.setMessage(e.getMessage());r.setCode(ErrorInfo.ERROR);r.setUrl(req.getRequestURL().toString());return r;} }

然后我們自定義一個(gè)自定義異常類BaseException:

public class BaseException extends Exception {public BaseException(String message) {super(message);} }

然后在Controller層定義一個(gè)方法測試:

@Controller @RequestMapping("/error") public class ErrController {@RequestMapping("/base")public String base() throws BaseException {throw new BaseException("系統(tǒng)異常,請(qǐng)稍后重試。");} }

老規(guī)矩,啟動(dòng)項(xiàng)目,請(qǐng)求接口可以看到結(jié)果:

你也可以不自定義異常BaseException,而直接攔截常見的各種異常都可以。所以這是一個(gè)非常靈活的異常處理器。你也可以做跳轉(zhuǎn)頁面,返回ModelAndView即可(以免篇幅過長就不演示了,哈哈)。

小結(jié)

經(jīng)過以上的演示后我們學(xué)習(xí)了SpringMVC四種異常處理器的工作機(jī)制,最后這種作為程序員我覺得是必須掌握的,前面的簡單映射異常處理器和狀態(tài)映射處理器可以選擇性掌握,默認(rèn)的異常處理器了解即可。

那這么多異常處理器,究竟是如何工作的呢?為什么是設(shè)計(jì)一個(gè)接口,下面有一個(gè)抽象類加上四個(gè)實(shí)現(xiàn)子類呢?接下來我們通過源碼分析來揭開謎底!

源碼分析

源碼分析從哪里入手呢?在SpringMVC中,其實(shí)你想都不用想,肯定在DispatcherServlet類里。經(jīng)過我順藤摸瓜,我定位在了processHandlerException()方法。怎么定位的呢?其實(shí)很簡單,看源碼:

private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,@Nullable Exception exception) throws Exception {boolean errorView = false;//異常不為空if (exception != null) {if (exception instanceof ModelAndViewDefiningException) {logger.debug("ModelAndViewDefiningException encountered", exception);mv = ((ModelAndViewDefiningException) exception).getModelAndView();}else {Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);//關(guān)鍵點(diǎn):執(zhí)行異常處理mv = processHandlerException(request, response, handler, exception);//省略...}}//省略...}

processHandlerException()

就是這個(gè)直接的一個(gè)if-else判斷,那個(gè)processHandlerException()方法又是怎么處理的呢?

@Nullable protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response,@Nullable Object handler, Exception ex) throws Exception {ModelAndView exMv = null;//判斷異常處理器的集合是否為空if (this.handlerExceptionResolvers != null) {//不為空則遍歷異常處理器 for (HandlerExceptionResolver handlerExceptionResolver : this.handlerExceptionResolvers) {//調(diào)用異常處理器的resolveException()方法進(jìn)行處理異常exMv = handlerExceptionResolver.resolveException(request, response, handler, ex);//判斷返回的ModelAndView是否為null,不為null則跳出循環(huán),為null則繼續(xù)下一個(gè)異常處理器if (exMv != null) {break;}}}//如果ModelAndView不為空if (exMv != null) {if (exMv.isEmpty()) {//設(shè)置異常信息提示request.setAttribute(EXCEPTION_ATTRIBUTE, ex);return null;}//如果返回的ModelAndView不包含viewif (!exMv.hasView()) {//設(shè)置一個(gè)默認(rèn)的視圖 String defaultViewName = getDefaultViewName(request);if (defaultViewName != null) {exMv.setViewName(defaultViewName);}}//省略...//返回異常的ModelAndView return exMv;}throw ex; }

這不就是責(zé)任鏈模式嗎!提前加載異常處理器到handlerExceptionResolvers集合中,然后遍歷去執(zhí)行,能處理就處理,不能處理就跳到下一個(gè)異常處理器處理。

那接下來我們就有一個(gè)問題了,handlerExceptionResolvers集合是怎么加載異常處理器的?這個(gè)問題很簡單,就是使用DispatcherServlet.properties配置文件。這個(gè)文件真的很重要!!!

org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver,org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver

默認(rèn)是加載以上三種異常處理器到集合中,所以只要帶有@ControllerAdvice、@ExceptionHandler、@ResponseStatus注解的都會(huì)被掃描。SimpleMappingExceptionResolver則是通過xml文件(當(dāng)然也可以使用@Configuration)去配置。

resolveException()

其實(shí)在resolveException()處理異常的方法中,還使用了模板模式。

@Override@Nullablepublic ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response,@Nullable Object handler, Exception ex) {//省略...//預(yù)處理prepareResponse(ex, response);//調(diào)用了一個(gè)抽象方法,抽象方法由子類去實(shí)現(xiàn)ModelAndView result = doResolveException(request, response, handler, ex);//省略...}

抽象方法doResolveException(),由子類實(shí)現(xiàn)。

@Nullable protected abstract ModelAndView doResolveException(HttpServletRequest request,HttpServletResponse response, @Nullable Object handler, Exception ex);

怎么識(shí)別模板方法,其實(shí)很簡單,只要看到抽象類,有個(gè)具體方法里面調(diào)用了抽象方法,那很大可能就是模板模式。抽象方法就是模板方法,由子類實(shí)現(xiàn)。

子類我們都知道就是那四個(gè)異常處理器實(shí)現(xiàn)類了。

總結(jié)

用流程圖概括一下:

經(jīng)過以上的學(xué)習(xí)后,我們知道只需要把異常處理器加到集合中,就可以執(zhí)行。所以我們可以使用直接實(shí)現(xiàn)HandlerExceptionResolver接口的方式來實(shí)現(xiàn)異常處理器。

實(shí)現(xiàn)HandlerExceptionResolver接口實(shí)現(xiàn)全局異常處理

首先自定一個(gè)異常類MyException。

public class MyException extends Exception {public MyException(String message) {super(message);} }

然后實(shí)現(xiàn)HandlerExceptionResolver接口定義一個(gè)異常處理器。

//注冊(cè)異常處理器到Spring容器中 @Component public class MyExceptionHandler implements HandlerExceptionResolver {@Overridepublic ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {try {//如果屬于MyException異常,則輸出異常提示到頁面if (ex instanceof MyException) {response.setContentType("text/html;charset=utf-8");response.getWriter().println(ex.getMessage());//這里返回null,不做處理。也可以返回ModelAndView跳轉(zhuǎn)頁面return null;}} catch (IOException e) {e.printStackTrace();}return null;} }

然后在Controller層定義一個(gè)方法測試:

@Controller @RequestMapping("/error") public class ErrController {@RequestMapping("/myEx")public String myEx() throws MyException {System.out.println("執(zhí)行myEx()");throw new MyException("自定義異常提示信息");} }

啟動(dòng)項(xiàng)目,請(qǐng)求接口,我們可以看到:

作者:Java技術(shù)愛好者
鏈接:SpringMVC全局異常處理機(jī)制 - 云+社區(qū) - 騰訊云
來源:騰訊云社區(qū)

總結(jié)

以上是生活随笔為你收集整理的bean加载时调用@value时会出现空指针异常_SpringMVC全局异常处理机制的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。