異常處理問題分析
異常如何處理
問題引入
-
針對程式碼中的異常,常規有兩種處理方式,一種throws直接丟擲,另一種try..catch捕獲。
-
在java專案中,有可能存在人為邏輯的異常,也可能為取得異常的詳情,或是保證程式在異常時繼續向下執行,會採用第二種處理方式。
-
但是,程式碼中每一處異常都來捕獲,會使程式碼什麼冗餘且不利於維護。
解決思路
-
定義一個全域性異常處理類,返回統一規範的異常資訊;
-
處理邏輯是,先判定是否會出現異常,再執行後續具體的業務。
業務舉例
本文主要為了實現全域性異常處理的邏輯,只舉簡單業務
某公司部門需增加員工,處理流程:1先根據員工編號查詢員工物件,2判斷員工物件是否有資訊,即是否不為空,3若有資訊,則說明已存在,無需再新增,若不是,則直接新增。
程式碼如下:
public class MyService {
// 注入dao層
@Autowired
EmployeeecMapper employeeecMapper;
/**
* 新增員工資訊
* @param employee 員工物件
* @return 影響的行數
*/
public int add(Employee employee) {
// 根據id查詢員工物件
Employeeec emp = employeeecMapper.selectByPrimaryKey(employee.getId());
// 判斷是否已有該員工
if (emp != null){
// 已有,丟擲異常,異常資訊為已有該員工
throw new RuntimeException("異常程式碼:1201,錯誤資訊:該員工已存在");
}
// 沒有,插入該員工
return employeeecMapper.insert(emp);
}
}
複製程式碼
異常處理流程
業務中存在執行時異常和業務邏輯異常,前者不執行時很難察覺,後者在遍及業務時就可以定義出來,因此異常分為不可預知異常和可知異常。流程如下:
- 自定義全域性異常類,使用
@ControllerAdvice
,控制器增強 - 自定義錯誤程式碼及錯誤資訊,兩種異常最終會採用統一的資訊格式來表示,錯誤程式碼+錯誤資訊。
- 對於可預知的異常由程式設計師在程式碼中主動丟擲,由
SpringMVC
統一捕獲。 - 不可預知異常通常是由於系統出現bug、或一些外界因素(如網路波動、伺服器當機等),異常型別為
RuntimeException
型別(執行時異常)。
可知異常
定義異常資訊類,變數為錯誤程式碼和錯誤資訊,捕獲自定義異常時,直接將該物件返回
不可知異常
定義一個map,將常見的異常存入其中,並定義錯誤程式碼。對於其他不常見的異常,即map中沒有的,同一一個異常物件返回即可。
異常處理程式碼流程
可知異常
1、定義列印異常資訊與返回結果的介面
public interface ResultCode {
// 操作是否成功
boolean success();
// 操作結果程式碼
long code();
// 提示資訊
String message();
}
複製程式碼
public interface Response {
public static final boolean SUCCESS = true;
public static final int SUCCESS_CODE = 10000;
}
複製程式碼
2、定義列印異常資訊的列舉類和返回結果類
@ToString
public enum CommonCode implements ResultCode {
NO_PAGE(false,404,"沒有資訊"),
FAIL(false,500,"操作失敗!"),
SUCCESS(true,200,"操作成功!");
// 結果資訊
boolean success;
long code;
String message;
// 帶參構造
CommonCode(boolean success, long code, String message) {
this.success = success;
this.code = code;
this.message = message;
}
@Override
public boolean success() {
return true;
}
@Override
public long code() {
return code;
}
@Override
public String message() {
return message;
}
}
複製程式碼
@Data
@ToString
public class ResponseResult implements Response {
boolean success = SUCCESS;
long code = SUCCESS_CODE;
String message;
public ResponseResult(ResultCode resultCode){
this.success = resultCode.success();
this.code = resultCode.code();
this.message = resultCode.message();
}
}
複製程式碼
3、定義錯誤異常類
public class CustomException extends RuntimeException{
@Autowired
ResultCode resultCode;
// 帶參構造
public CustomException(ResultCode resultCode){
this.resultCode = resultCode;
}
// getter
public ResultCode getResultCode(){
return resultCode;
}
}
複製程式碼
4、定義異常丟擲類
public class ExceptionCast {
// 靜態方法
public static void cast(ResultCode resultCode){
throw new CustomException(resultCode);
}
}
複製程式碼
5、定義異常捕獲類,使用ControllerAdvice
控制器增強的註解,並在捕獲CustomException異常的方法上加ExceptionHandler
註解,即可捕獲該類的所有異常,返回json資料。
@ControllerAdvice
public class ExceptionCatch {
/**
* 捕獲CustomException類異常
* @param customException
* @return 結果資訊,json資料
*/
@ExceptionHandler(CustomException.class)
@ResponseBody
public ResponseResult customException(CustomException customException){
ResultCode resultCode = customException.getResultCode();
return new ResponseResult(resultCode);
}
}
複製程式碼
6、在業務中丟擲異常
public class MyService {
@Autowired
EmployeeecMapper employeeecMapper;
public int add(Employee employee) {
Employeeec emp = employeeecMapper.selectByPrimaryKey(employee.getId());
if (emp != null){
ExceptionCast.cast(CommonCode.FAIL);
}
return employeeecMapper.insert(emp);
}
}
複製程式碼
不可知異常處理
1、類似可知異常,先在CommonCode類中新增錯誤程式碼,如
UNAUTHORISE(false,510,"沒有許可權"),
複製程式碼
2、在異常捕獲類中新增不可知異常的捕獲方法。該方法中,定義一個只讀的map儲存異常型別的錯誤程式碼的對映,map中沒有的元素,統一用錯誤程式碼999來定義。
UNKNOWNERROR(false,999,"未知異常"),
複製程式碼
@ControllerAdvice
public class ExceptionCatch {
// 定義map,存貯常見錯誤資訊。該類map不可修改
private static ImmutableMap<Class<? extends Throwable>,ResultCode> EXCEPTIONS;
// 構建ImmutableMap
protected static ImmutableMap.Builder<Class<? extends Throwable>,ResultCode> builder = ImmutableMap.builder();
@ExceptionHandler(CustomException.class)
@ResponseBody
public ResponseResult customException(CustomException customException){
ResultCode resultCode = customException.getResultCode();
return new ResponseResult(resultCode);
}
/**
* 捕獲非自定義類異常
* @param exception
* @return
*/
@ExceptionHandler(Exception.class)
@ResponseBody
public ResponseResult exception(Exception exception){
// 記錄日誌
LOGGER.error("catch exception ==> ",exception.getMessage());
if (EXCEPTIONS == null){
EXCEPTIONS = builder.build();
}
ResultCode resultCode = EXCEPTIONS.get(exception.getClass());
if (resultCode != null){
return new ResponseResult(resultCode);
}else {
return new ResponseResult(CommonCode.UNKNOWNERROR);
}
}
static {
builder.put(HttpMessageNotReadableException.class, CommonCode.INVALID_PARAM);
}
}
複製程式碼
完成~~