前言
先谈谈“异常处理”这件事。下面有 2 份伪代码,对比下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
// ① 基于 if/else 判断
if (deletePage(page) == E_OK){
if (registry.deleteReference(page.name) == E_OK){
if (configKeys.deleteKey(page.name.makeKey()) == E_OK){
logger.log( "page deleted" );
} else {
logger.log( "configKey not deleted" );
}
} else {
logger.log( "deleteReference from registry failed" );
}
} else {
logger.log( "delete failed" );
return E_RROR;
}
|
1
2
3
4
5
6
7
8
|
// ② 基于异常处理
try {
deletePage(page);
registry.deleteReference(page.name);
configKeys.deleteKey(page.name.makeKey());
} catch (Exception e){
logError(e);
}
|
可以看出,如果使用异常替代返回错误码,错误处理代码就能从主路径逻辑中分离出来,得到简化!
②中,基于异常处理的代码真的好吗?其实是丑陋不堪的,它搞乱了代码结构,把错误处理与正常流程混为一谈。最好把 try 和 catch 代码块的主体部分抽离出来,形成另外的函数。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
// ③ 优雅的异常处理逻辑
public void delete(Page page){
try {
deletePageAndAllReferences(page);
} catch (Exception e){
logError(e);
}
}
private void deletePageAndAllReferences(Page page) throw Exception{
deletePage(page);
registry.deleteReference(page.name);
configKeys.deleteKey(page.name.makeKey());
}
private void logError(Exception e){
logger.log(e.getMessage());
}
|
③中,函数各司其职,更易于理解和修改了。
总结:使用异常而不是错误码,优雅地使用异常!函数应该只做一件事,处理错误就是一件事。因此,处理错误的函数不该做其他事!
在 Spring Boot 中处理异常
1、默认的异常处理
例如 401,404,500,5XX 等异常,Spring Boot 默认会跳转到预配置的页面,此处以 thymeleaf 模板引擎为例:
1
2
3
4
5
6
|
+ resources
+ templates
+ error
- 401.html
- 404.html
- 500.html
|
只需在 resources/templates/error/ 路径下添加对应的html文件即可。
2、局部异常处理
局部异常一般处理业务逻辑出现的异常情况,在 Controller 下使用 @ExceptionHandler 注解来处理异常。举个小例子:
先定义 ResponseBean 和 ExceptionEnum 两个对象,辅助完成优雅的代码。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
/**
* 统一响应
* @author anoy
*/
public class ResponseBean<T> {
private int code;
private String message;
private T data;
public ResponseBean(){}
public ResponseBean(ExceptionEnum exceptionEnum){
this .code = exceptionEnum.getCode();
this .message = exceptionEnum.getMessage();
}
// 省略 setter/getter
}
|
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
|
/**
* 异常类型枚举
* @author anoy
*/
public enum ExceptionEnum {
GIRL_FRIEND_NOT_FOUND( 100000 , "girl friend not found" );
private int code;
private String message;
ExceptionEnum( int code, String message){
this .code = code;
this .message = message;
}
public int getCode() {
return code;
}
public String getMessage() {
return message;
}
}
|
今天七夕,写个 GirlFriendNotFoundException(很有同感,是不是?)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
@Controller
public class UserController {
@RequestMapping ( "/friend/{id}" )
public String friend( @PathVariable ( "id" ) Long id) throws GirlFriendNotFoundException {
if (id == 1L){
throw new GirlFriendNotFoundException();
}
return "friend" ;
}
@ExceptionHandler (GirlFriendNotFoundException. class )
@ResponseBody
public ResponseBean handleGirlFriendNotFound(GirlFriendNotFoundException exception){
loggerError(exception);
return new ResponseBean(ExceptionEnum.GIRL_FRIEND_NOT_FOUND);
}
private void logError(Exception e){
logger.error(e.getMessage());
}
}
|
3、全局异常处理
个人观点:全局异常应该处理系统故障级别的问题,像参数校验这种类型的异常,应该作为局部异常来处理,例如 Redis 连接断开,无法请求数据,这种异常就应该当做全局异常来处理,在异常处理的逻辑中,还应该添加通知到开发人员的功能,方便开发人员及时处理错误!
全局异常处理,使用 @ControllerAdvice
和 @ExceptionHandler
来配合。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler (RedisConnectionFailureException. class )
public void handlerRedisConnectionFailureException(RedisConnectionFailureException exception){
logError(exception);
noticeToDev();
}
private void logError(Exception e){
logger.error(e.getMessage());
}
private void noticeToDev(){
// 通知具体开发人员
}
}
|
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持服务器之家。
原文链接:http://www.jianshu.com/p/5b7489503080?utm_source=tuicool&utm_medium=referral