Spring boot集中異常處理方法實(shí)例
集中異常處理
以下代碼可在github獲取:https://github.com/Syske/learning-dome-code/tree/master/springboot-learning
方式一:ExceptionHandle
定義自己的異常類型,根據(jù)不同類型做不同處理,比如我定義的MyException:
public class MyException extends RuntimeException {
public MyException(String msg) {
super(msg);
}
}
然后通過(guò)MyExceptionHandle處理該異常,需要注意的是異常不能在filter中拋出,拋出也沒(méi)法捕獲
@RestControllerAdvice
public class MyExceptionHandle {
@ExceptionHandler(MyException.class)
public Result exceptionHandle(MyException e) {
return Result.getFailed( "system error:MyException" + e.getMessage());
}
}
在controller、service以及攔截器的預(yù)處理方法中都可以完美捕獲,這里特殊說(shuō)下攔截器:
public class MyInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
//throw new MyException("攔截器錯(cuò)誤:MyInterceptor");
// 這里的異常會(huì)完美捕獲,并返回
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {
throw new MyException("攔截器錯(cuò)誤:MyInterceptor");
/** 能捕獲異常信息并返回給客戶端,但并不會(huì)覆蓋已經(jīng)請(qǐng)求成功的返回結(jié)果,但會(huì)包含在返回結(jié)果中,比如我的返回結(jié)果:
{"code":1,"success":true,"msg":"請(qǐng)求成功","result":true}{"code":0,"success":false,"msg":"system error:MyException攔截器錯(cuò)誤:MyInterceptor","result":null}
*/
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
throw new MyException("攔截器錯(cuò)誤:MyInterceptor");
// 這里拋出的異常并不能被捕獲,會(huì)直接在后臺(tái)拋出,相當(dāng)于回掉函數(shù),請(qǐng)求結(jié)果已經(jīng)返回
}
}
上面的解釋已經(jīng)夠清楚了,就不做過(guò)多說(shuō)明了。剛剛我們說(shuō)了,上面的這種方式,對(duì)于filter是不起作用的,下面我們說(shuō)的這種方式,主要就是針對(duì)filter來(lái)說(shuō)的
方式二:ExceptionFilter
定義一個(gè)controller,請(qǐng)求路徑可以自己指定,比如/error/exthrow:
@Controller
public class ExceptionController {
@RequestMapping("/error/exthrow")
public void rethrow(HttpServletRequest request) throws Exception {
throw ((Exception) request.getAttribute("filter.error"));
}
}
再定義一個(gè)異常攔截器,在需要拋出異常的攔截器中直接拋出異常,然后在異常攔截器中try-catch,發(fā)生異常時(shí)直接轉(zhuǎn)發(fā)至前面定義的異常controller,這里需要注意的是,如果你的filter是實(shí)現(xiàn)Filter或者繼承OncePerRequestFilter,那你不需要任何處理,直接request.setAttribute("filter.error", e)就可以了。
由于我把自己的filter交給shiro管理,而且是繼承BasicHttpAuthenticationFilter的,不知到什么原因,直接catch到的異常類型是ServletException,為了拿到真正的異常信息,我需要通過(guò)getCause()方法獲取filter中拋出的異常。因?yàn)閏ontroller拋出的異常最后還是會(huì)交給我們定義的MyExceptionHandle去處理,如果獲取到的異常不是我們自定義的異?;蛘咚淖宇惖脑?,就會(huì)返回500錯(cuò)誤(在這個(gè)示例前,我以為所有的filter都是這樣的,后來(lái)實(shí)踐后發(fā)現(xiàn)并不是這樣😂)。
@Component
public class ExceptionFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
try {
chain.doFilter(request, response);
} catch (Exception e) {
request.setAttribute("filter.error", e);
//將異常分發(fā)到/error/exthrow控制器
request.getRequestDispatcher("/error/exthrow").forward(request, response);
}
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void destroy() {
}
}
我的filter:
實(shí)現(xiàn)Filter接口:
public class MyFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
throw new MyException("MyFilter過(guò)濾器拋出異常");
//filterChain.doFilter(servletRequest, servletResponse);
}
}
filter2繼承OncePerRequestFilter:
public class MyFilter2 extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
throw new MyException("MyFilter2異常");
}
}
filter配置類:
@Bean
public FilterRegistrationBean myFilterRegistration() {
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(new MyFilter());
registration.setName("myFilter");
registration.addUrlPatterns("/*");
//此處盡量小,要比其他Filter靠前
registration.setOrder(1);
return registration;
}
@Bean
public FilterRegistrationBean myFilter2Registration() {
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(new MyFilter2());
registration.setName("myFilter2");
registration.addUrlPatterns("/*");
//此處盡量小,要比其他Filter靠前
registration.setOrder(2);
return registration;
}
/**
* 配置攔截器
* @return
*/
@Bean
public FilterRegistrationBean exceptionFilterRegistration() {
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(new ExceptionFilter());
registration.setName("exceptionFilter");
//此處盡量小,要比其他Filter靠前
registration.setOrder(-1);
return registration;
}
方式三:BasicErrorController
其實(shí)spring boot原生提供了異常集中處理,我們經(jīng)常會(huì)看到:

但是這種方式不夠友好,而且大部分情況不滿足我們的需求,我們可以通過(guò)繼承這個(gè)controller,然后重寫error方法或者errorHtml方法,或者兩個(gè)都重寫,區(qū)別是errorHtml是處理請(qǐng)求頭為text/html的請(qǐng)求發(fā)生的異常,而error是除了這個(gè)之外的其他異常。
下面是我定義的baseController,error部分返回的結(jié)果是空,還需要進(jìn)一步的研究:
@RestController
@RequestMapping(value = "error")
public class MyBaseErrorController extends BasicErrorController {
public MyBaseErrorController(ErrorAttributes errorAttributes) {
super(errorAttributes, new ErrorProperties());
}
@Override
@RequestMapping(produces = {MediaType.ALL_VALUE})
public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
return new ResponseEntity<Map<String, Object>>(Result.failedResultMap(000, "未知錯(cuò)誤"), HttpStatus.OK);
}
@RequestMapping(
produces = {"text/html"}
)
public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {
HttpStatus status = this.getStatus(request);
response.setStatus(status.value());
return null;
}
}
最后,對(duì)于以上問(wèn)題我最后的解決方法是繼承ErrorController,然后定義errorHtml和error,更重要的是@RequestMapping注解,然后在方法中response寫入返回值,這種方式不夠優(yōu)雅:
@RestController
@RequestMapping(value = "error")
public class MyBaseErrorController implements ErrorController {
private static final String path_default = "/error";
@Autowired
private ErrorAttributes errorAttributes;
@RequestMapping(produces = {MediaType.ALL_VALUE})
public void error(HttpServletRequest request, HttpServletResponse response) {
setJsonError(response);
}
@RequestMapping(
produces = {"text/html"}
)
public void errorHtml(HttpServletRequest request, HttpServletResponse response) {
setJsonError(response);
}
@Override
public String getErrorPath() {
return path_default;
}
private void setJsonError(HttpServletResponse response) {
PrintWriter writer = null;
try {
response.setStatus(200);
response.setHeader("Content-type", "text/html;charset=UTF-8");
response.setCharacterEncoding("UTF-8");
writer = response.getWriter();
writer.write(JSON.toJSONString(Result.getFailed("未知錯(cuò)誤", null)));
} catch (IOException e) {
e.printStackTrace();
} finally {
if (writer != null) {
writer.close();
}
}
}
}
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
JeecgBoot框架升級(jí)至Spring?Boot3的實(shí)戰(zhàn)步驟
本文主要介紹了JeecgBoot框架升級(jí)至Spring?Boot3的實(shí)戰(zhàn)步驟,從?2.7.10升級(jí)到3.1.5有以下幾個(gè)點(diǎn)需要注意,下面就來(lái)詳細(xì)的介紹一下,感興趣的可以了解一下2024-04-04
詳解Java如何實(shí)現(xiàn)企業(yè)微信審批流程
這篇文章主要使用了一個(gè)Java示例代碼,來(lái)向大家展示如何在企業(yè)微信中實(shí)現(xiàn)審批流程,文中的示例代碼簡(jiǎn)潔易懂,有需要的小伙伴可以參考下2024-11-11
Windows安裝Maven并配置環(huán)境的詳細(xì)步驟
Maven是一個(gè)非常流行的構(gòu)建和項(xiàng)目管理工具,用于Java開發(fā),它提供了一個(gè)強(qiáng)大的依賴管理系統(tǒng)和一系列標(biāo)準(zhǔn)化的構(gòu)建生命周期,本文將指導(dǎo)您如何在Windows操作系統(tǒng)上安裝和配置Maven,需要的朋友可以參考下2023-05-05
SpringBoot項(xiàng)目實(shí)現(xiàn)關(guān)閉數(shù)據(jù)庫(kù)配置和springSecurity
這篇文章主要介紹了SpringBoot項(xiàng)目實(shí)現(xiàn)關(guān)閉數(shù)據(jù)庫(kù)配置和springSecurity的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-08-08
java獲取ip地址與網(wǎng)絡(luò)接口的方法示例
這篇文章主要給大家介紹了關(guān)于利用java如何獲取ip地址與網(wǎng)絡(luò)接口的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。2018-01-01
解決Spring?Security集成knife4j訪問(wèn)接口文檔出現(xiàn)403的問(wèn)題
這篇文章主要給大家介紹了如何解決Spring?Security集成knife4j訪問(wèn)接口文檔出現(xiàn)403的問(wèn)題,文中有詳細(xì)的解決方案,有需要的朋友可以參考閱讀下2023-07-07
Java BigDecimal解決double精度丟失的問(wèn)題
我們?cè)谌粘i_發(fā)中, 有很多時(shí)候會(huì)遇到小數(shù)(double類型)精確計(jì)算,本文主要介紹了Java BigDecimal解決double精度丟失的問(wèn)題,具有一定的參考價(jià)值,感興趣的可以了解一下2023-11-11

