把 @ExceptionHandler、HandlerExceptionResolver、@controlleradvice 三兄弟放在一起來寫更有比較性。這三個東西都是用來處理異常的,但是它們使用的場景都不一樣。看本文給你詳細的講解,再也不怕面試被問到了!
這三個注解都是來自於 SpringMVC 的,都能進行異常處理。
Java 程序員都非常的痛恨異常,很多人討厭 Java 就是因為它的異常處理機制。到處的 try-catch-finally,再不是就是到處拋出異常。
所以 Spring 深知 Java 的疼點,推出了:@ExceptionHandler、HandlerExceptionResolver、@controlleradvice 來方便我們處理一些異常!
@ExceptionHandler 注解
用於局部方法捕獲,與拋出異常的方法處於同一個 Controller 類。源碼如下:
1
2
3
4
5
6
|
@Target
({ElementType.METHOD})
@Retention
(RetentionPolicy.RUNTIME)
@Documented
public
@interface
ExceptionHandler {
Class<?
extends
Throwable>[] value()
default
{};
}
|
從源碼中可以看出,@ExceptionHandler 注解只能作用為對象的方法上,並且在運行時有效,value() 可以指定異常類。由該注解注釋的方法可以具有靈活的輸入參數。
異常參數可以包括一般的異常或特定的異常(即自定義異常),如果注解沒有指定異常類,會默認進行映射。
用法代碼如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
@Controller
public
class
XttblogController {
@ExceptionHandler
({NullPointerException.
class
})
public
String exception(NullPointerException e) {
System.out.println(e.getMessage());
e.printStackTrace();
return
"null pointer exception"
;
}
@RequestMapping
(
"test"
)
public
void
test() {
throw
new
NullPointerException(
"出錯了!"
);
}
}
|
上面這段代碼只會捕獲 XttblogController 類中的 NullPointerException 異常。
HandlerExceptionResolver 接口
HandlerExceptionResolver 是 Spring 提供的一個接口。它可以用來處理全局異常!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
@Component
public
class
GlobalExceptionResolver
implements
HandlerExceptionResolver{
private
ObjectMapper objectMapper;
public
CustomMvcExceptionHandler() {
objectMapper =
new
ObjectMapper();
}
@Override
public
ModelAndView resolveException(HttpServletRequest request,
HttpServletResponse response,Object o, Exception ex) {
response.setStatus(
200
);
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
response.setCharacterEncoding(
"UTF-8"
);
response.setHeader(
"Cache-Control"
,
"no-cache, must-revalidate"
);
Map<String, Object> map =
new
HashMap<>();
if
(ex
instanceof
NullPointerException) {
map.put(
"code"
, ResponseCode.NP_EXCEPTION);
}
else
if
(ex
instanceof
IndexOutOfBoundsException) {
map.put(
"code"
, ResponseCode.INDEX_OUT_OF_BOUNDS_EXCEPTION);
}
else
{
map.put(
"code"
, ResponseCode.CATCH_EXCEPTION);
}
try
{
map.put(
"data"
, ex.getMessage());
response.getWriter().write(objectMapper.writeValueAsString(map));
}
catch
(Exception e) {
e.printStackTrace();
}
return
new
ModelAndView();
}
}
|
在 Spring 源碼中,我們可以看出它會獲取一個實現了 HandlerExceptionResolver 接口的列表 List<HandlerExceptionResolver> resolvers; 如果這個列表不為空,則循環處理其中的異常。
HandlerExceptionResolve 雖然能夠處理全局異常,但是 Spring 官方不推薦使用它。
@controlleradvice 注解
另外一個能夠處理全局異常的就是 @controlleradvice 注解了。
@controlleradvice 注解根據它的源碼,我們知道它也只能作用在類上,並且作用於運行時。下面我們來看一個例子。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
@ControllerAdvice
public
class
ExceptionController {
@ExceptionHandler
(RuntimeException.
class
)
public
ModelAndView handlerRuntimeException(RuntimeException ex) {
if
(ex
instanceof
MaxUploadSizeExceededException) {
return
new
ModelAndView(
"error"
).addObject(
"msg"
,
"文件太大!"
);
}
return
new
ModelAndView(
"error"
).addObject(
"msg"
,
"未知錯誤:"
+ ex);
}
@ExceptionHandler
(Exception.
class
)
public
ModelAndView handlerMaxUploadSizeExceededException(Exception ex) {
if
(ex !=
null
) {
return
new
ModelAndView(
"error"
).addObject(
"msg"
, ex);
}
return
new
ModelAndView(
"error"
).addObject(
"msg"
,
"未知錯誤:"
+ ex);
}
}
|
需要注意的是,@ControllerAdvice 一般是和 @ExceptionHandler 組合在一起使用的。官方也推薦用這種方式處理統一全局異常。