為什么不建議用try catch處理異常?
1、這個阿里云工程師的甩鍋能力,真的超級高水平! 2、「中國加班第一樓」深圳科興萬人大撤離!拖主機帶屏幕,程序員公交上寫代碼! 3、SpringBoot + Redis:模擬 10w 人的秒殺搶單 4、研究所年入20萬,是什么體驗? 5、別瞎寫工具類了,Spring自帶的挺香的! 6、一口氣說出 Redis 16 個常見使用場景
文章來源:http://u6.gg/kpz4f


什么是統(tǒng)一異常處理

目標
統(tǒng)一異常處理實戰(zhàn)
| 用 Assert(斷言)替換 throw exception
@Test
????public?void?test1()?{
????????...
????????User?user?=?userDao.selectById(userId);
????????Assert.notNull(user,?"用戶不存在.");
????????...
????}
????@Test
????public?void?test2()?{
????????//?另一種寫法
????????User?user?=?userDao.selectById(userId);
????????if?(user?==?null)?{
????????????throw?new?IllegalArgumentException("用戶不存在.");
????????}
????}
public?abstract?class?Assert?{
????public?Assert()?{
????}
????public?static?void?notNull(@Nullable?Object?object,?String?message)?{
????????if?(object?==?null)?{
????????????throw?new?IllegalArgumentException(message);
????????}
????}
}
Assert
public?interface?Assert?{
????/**
?????*?創(chuàng)建異常
?????*?@param?args
?????*?@return
?????*/
????BaseException?newException(Object...?args);
????/**
?????*?創(chuàng)建異常
?????*?@param?t
?????*?@param?args
?????*?@return
?????*/
????BaseException?newException(Throwable?t,?Object...?args);
????/**
?????*?斷言對象
obj非空。如果對象obj為空,則拋出異常
?????*
?????*?@param?obj?待判斷對象
?????*/
????default?void?assertNotNull(Object?obj)?{
????????if?(obj?==?null)?{
????????????throw?newException(obj);
????????}
????}
????/**
?????*?斷言對象
obj非空。如果對象obj為空,則拋出異常
?????*?異常信息
message支持傳遞參數(shù)方式,避免在判斷之前進行字符串拼接操作
?????*
?????*?@param?obj?待判斷對象
?????*?@param?args?message占位符對應(yīng)的參數(shù)列表
?????*/
????default?void?assertNotNull(Object?obj,?Object...?args)?{
????????if?(obj?==?null)?{
????????????throw?newException(args);
????????}
????}
}
| 善解人意的 Enum
public?interface?IResponseEnum?{
????int?getCode();
????String?getMessage();
}
/**
?*?業(yè)務(wù)異常
?*?業(yè)務(wù)處理時,出現(xiàn)異常,可以拋出該異常
?*/
public?class?BusinessException?extends??BaseException?{
????private?static?final?long?serialVersionUID?=?1L;
????public?BusinessException(IResponseEnum?responseEnum,?Object[]?args,?String?message)?{
????????super(responseEnum,?args,?message);
????}
????public?BusinessException(IResponseEnum?responseEnum,?Object[]?args,?String?message,?Throwable?cause)?{
????????super(responseEnum,?args,?message,?cause);
????}
}
public?interface?BusinessExceptionAssert?extends?IResponseEnum,?Assert?{
????@Override
????default?BaseException?newException(Object...?args)?{
????????String?msg?=?MessageFormat.format(this.getMessage(),?args);
????????return?new?BusinessException(this,?args,?msg);
????}
????@Override
????default?BaseException?newException(Throwable?t,?Object...?args)?{
????????String?msg?=?MessageFormat.format(this.getMessage(),?args);
????????return?new?BusinessException(this,?args,?msg,?t);
????}
}
@Getter
@AllArgsConstructor
public?enum?ResponseEnum?implements?BusinessExceptionAssert?{
????/**
?????*?Bad?licence?type
?????*/
????BAD_LICENCE_TYPE(7001,?"Bad?licence?type."),
????/**
?????*?Licence?not?found
?????*/
????LICENCE_NOT_FOUND(7002,?"Licence?not?found.")
????;
????/**
?????*?返回碼
?????*/
????private?int?code;
????/**
?????*?返回消息
?????*/
????private?String?message;
}
BAD_LICENCE_TYPE
LICENCE_NOT_FOUND
/**
?????*?校驗{@link?Licence}存在
?????*?@param?licence
?????*/
????private?void?checkNotNull(Licence?licence)?{
????????ResponseEnum.LICENCE_NOT_FOUND.assertNotNull(licence);
????}
private?void?checkNotNull(Licence?licence)?{
????????if?(licence?==?null)?{
????????????throw?new?LicenceNotFoundException();
????????????//?或者這樣
????????????throw?new?BusinessException(7001,?"Bad?licence?type.");
????????}
????}
| 定義統(tǒng)一異常處理器類
@Slf4j
@Component
@ControllerAdvice
@ConditionalOnWebApplication
@ConditionalOnMissingBean(UnifiedExceptionHandler.class)
public?class?UnifiedExceptionHandler?{
????/**
?????*?生產(chǎn)環(huán)境
?????*/
????private?final?static?String?ENV_PROD?=?"prod";
????@Autowired
????private?UnifiedMessageSource?unifiedMessageSource;
????/**
?????*?當前環(huán)境
?????*/
????@Value("${spring.profiles.active}")
????private?String?profile;
????/**
?????*?獲取國際化消息
?????*
?????*?@param?e?異常
?????*?@return
?????*/
????public?String?getMessage(BaseException?e)?{
????????String?code?=?"response."?+?e.getResponseEnum().toString();
????????String?message?=?unifiedMessageSource.getMessage(code,?e.getArgs());
????????if?(message?==?null?||?message.isEmpty())?{
????????????return?e.getMessage();
????????}
????????return?message;
????}
????/**
?????*?業(yè)務(wù)異常
?????*
?????*?@param?e?異常
?????*?@return?異常結(jié)果
?????*/
????@ExceptionHandler(value?=?BusinessException.class)
????@ResponseBody
????public?ErrorResponse?handleBusinessException(BaseException?e)?{
????????log.error(e.getMessage(),?e);
????????return?new?ErrorResponse(e.getResponseEnum().getCode(),?getMessage(e));
????}
????/**
?????*?自定義異常
?????*
?????*?@param?e?異常
?????*?@return?異常結(jié)果
?????*/
????@ExceptionHandler(value?=?BaseException.class)
????@ResponseBody
????public?ErrorResponse?handleBaseException(BaseException?e)?{
????????log.error(e.getMessage(),?e);
????????return?new?ErrorResponse(e.getResponseEnum().getCode(),?getMessage(e));
????}
????/**
?????*?Controller上一層相關(guān)異常
?????*
?????*?@param?e?異常
?????*?@return?異常結(jié)果
?????*/
????@ExceptionHandler({
????????????NoHandlerFoundException.class,
????????????HttpRequestMethodNotSupportedException.class,
????????????HttpMediaTypeNotSupportedException.class,
????????????MissingPathVariableException.class,
????????????MissingServletRequestParameterException.class,
????????????TypeMismatchException.class,
????????????HttpMessageNotReadableException.class,
????????????HttpMessageNotWritableException.class,
????????????//?BindException.class,
????????????//?MethodArgumentNotValidException.class
????????????HttpMediaTypeNotAcceptableException.class,
????????????ServletRequestBindingException.class,
????????????ConversionNotSupportedException.class,
????????????MissingServletRequestPartException.class,
????????????AsyncRequestTimeoutException.class
????})
????@ResponseBody
????public?ErrorResponse?handleServletException(Exception?e)?{
????????log.error(e.getMessage(),?e);
????????int?code?=?CommonResponseEnum.SERVER_ERROR.getCode();
????????try?{
????????????ServletResponseEnum?servletExceptionEnum?=?ServletResponseEnum.valueOf(e.getClass().getSimpleName());
????????????code?=?servletExceptionEnum.getCode();
????????}?catch?(IllegalArgumentException?e1)?{
????????????log.error("class?[{}]?not?defined?in?enum?{}",?e.getClass().getName(),?ServletResponseEnum.class.getName());
????????}
????????if?(ENV_PROD.equals(profile))?{
????????????//?當為生產(chǎn)環(huán)境,?不適合把具體的異常信息展示給用戶,?比如404.
????????????code?=?CommonResponseEnum.SERVER_ERROR.getCode();
????????????BaseException?baseException?=?new?BaseException(CommonResponseEnum.SERVER_ERROR);
????????????String?message?=?getMessage(baseException);
????????????return?new?ErrorResponse(code,?message);
????????}
????????return?new?ErrorResponse(code,?e.getMessage());
????}
????/**
?????*?參數(shù)綁定異常
?????*
?????*?@param?e?異常
?????*?@return?異常結(jié)果
?????*/
????@ExceptionHandler(value?=?BindException.class)
????@ResponseBody
????public?ErrorResponse?handleBindException(BindException?e)?{
????????log.error("參數(shù)綁定校驗異常",?e);
????????return?wrapperBindingResult(e.getBindingResult());
????}
????/**
?????*?參數(shù)校驗異常,將校驗失敗的所有異常組合成一條錯誤信息
?????*
?????*?@param?e?異常
?????*?@return?異常結(jié)果
?????*/
????@ExceptionHandler(value?=?MethodArgumentNotValidException.class)
????@ResponseBody
????public?ErrorResponse?handleValidException(MethodArgumentNotValidException?e)?{
????????log.error("參數(shù)綁定校驗異常",?e);
????????return?wrapperBindingResult(e.getBindingResult());
????}
????/**
?????*?包裝綁定異常結(jié)果
?????*
?????*?@param?bindingResult?綁定結(jié)果
?????*?@return?異常結(jié)果
?????*/
????private?ErrorResponse?wrapperBindingResult(BindingResult?bindingResult)?{
????????StringBuilder?msg?=?new?StringBuilder();
????????for?(ObjectError?error?:?bindingResult.getAllErrors())?{
????????????msg.append(",?");
????????????if?(error?instanceof?FieldError)?{
????????????????msg.append(((FieldError)?error).getField()).append(":?");
????????????}
????????????msg.append(error.getDefaultMessage()?==?null???""?:?error.getDefaultMessage());
????????}
????????return?new?ErrorResponse(ArgumentResponseEnum.VALID_ERROR.getCode(),?msg.substring(2));
????}
????/**
?????*?未定義異常
?????*
?????*?@param?e?異常
?????*?@return?異常結(jié)果
?????*/
????@ExceptionHandler(value?=?Exception.class)
????@ResponseBody
????public?ErrorResponse?handleException(Exception?e)?{
????????log.error(e.getMessage(),?e);
????????if?(ENV_PROD.equals(profile))?{
????????????//?當為生產(chǎn)環(huán)境,?不適合把具體的異常信息展示給用戶,?比如數(shù)據(jù)庫異常信息.
????????????int?code?=?CommonResponseEnum.SERVER_ERROR.getCode();
????????????BaseException?baseException?=?new?BaseException(CommonResponseEnum.SERVER_ERROR);
????????????String?message?=?getMessage(baseException);
????????????return?new?ErrorResponse(code,?message);
????????}
????????return?new?ErrorResponse(CommonResponseEnum.SERVER_ERROR.getCode(),?e.getMessage());
????}
}
進入 Controller 前的異常:handleServletException、handleBindException、handleValidException
自定義異常:handleBusinessException、handleBaseException
未知異常:handleException
異常處理器說明
| handleServletException
| handleBindException
| handleValidException
| handleBusinessException、handleBaseException
| handleException
異于常人的404


spring.mvc.throw-exception-if-no-handler-found=true
spring.resources.add-mappings=false
捕獲404對應(yīng)的異常
| 統(tǒng)一返回結(jié)果
| 驗證統(tǒng)一異常處理
@Service
public?class?LicenceService?extends?ServiceImpl<LicenceMapper,?Licence>?{
????@Autowired
????private?OrganizationClient?organizationClient;
????/**
?????*?查詢{@link?Licence}?詳情
?????*?@param?licenceId
?????*?@return
?????*/
????public?LicenceDTO?queryDetail(Long?licenceId)?{
????????Licence?licence?=?this.getById(licenceId);
????????checkNotNull(licence);
????????OrganizationDTO?org?=?ClientUtil.execute(()?->?organizationClient.getOrganization(licence.getOrganizationId()));
????????return?toLicenceDTO(licence,?org);
????}
????/**
?????*?分頁獲取
?????*?@param?licenceParam?分頁查詢參數(shù)
?????*?@return
?????*/
????public?QueryData?getLicences(LicenceParam?licenceParam)? {
????????String?licenceType?=?licenceParam.getLicenceType();
????????LicenceTypeEnum?licenceTypeEnum?=?LicenceTypeEnum.parseOfNullable(licenceType);
????????//?斷言,?非空
????????ResponseEnum.BAD_LICENCE_TYPE.assertNotNull(licenceTypeEnum);
????????LambdaQueryWrapper?wrapper?=?new?LambdaQueryWrapper<>();
????????wrapper.eq(Licence::getLicenceType,?licenceType);
????????IPage?page?=?this.page(new?QueryPage<>(licenceParam),?wrapper);
????????return?new?QueryData<>(page,?this::toSimpleLicenceDTO);
????}
????/**
?????*?新增{@link?Licence}
?????*?@param?request?請求體
?????*?@return
?????*/
????@Transactional(rollbackFor?=?Throwable.class)
????public?LicenceAddRespData?addLicence(LicenceAddRequest?request)?{
????????Licence?licence?=?new?Licence();
????????licence.setOrganizationId(request.getOrganizationId());
????????licence.setLicenceType(request.getLicenceType());
????????licence.setProductName(request.getProductName());
????????licence.setLicenceMax(request.getLicenceMax());
????????licence.setLicenceAllocated(request.getLicenceAllocated());
????????licence.setComment(request.getComment());
????????this.save(licence);
????????return?new?LicenceAddRespData(licence.getLicenceId());
????}
????/**
?????*?entity?->?simple?dto
?????*?@param?licence?{@link?Licence}?entity
?????*?@return?{@link?SimpleLicenceDTO}
?????*/
????private?SimpleLicenceDTO?toSimpleLicenceDTO(Licence?licence)?{
????????//?省略
????}
????/**
?????*?entity?->?dto
?????*?@param?licence?{@link?Licence}?entity
?????*?@param?org?{@link?OrganizationDTO}
?????*?@return?{@link?LicenceDTO}
?????*/
????private?LicenceDTO?toLicenceDTO(Licence?licence,?OrganizationDTO?org)?{
????????//?省略
????}
????/**
?????*?校驗{@link?Licence}存在
?????*?@param?licence
?????*/
????private?void?checkNotNull(Licence?licence)?{
????????ResponseEnum.LICENCE_NOT_FOUND.assertNotNull(licence);
????}
}
--?licence
INSERT?INTO?licence?(licence_id,?organization_id,?licence_type,?product_name,?licence_max,?licence_allocated)
VALUES?(1,?1,?'user','CustomerPro',?100,5);
INSERT?INTO?licence?(licence_id,?organization_id,?licence_type,?product_name,?licence_max,?licence_allocated)
VALUES?(2,?1,?'user','suitability-plus',?200,189);
INSERT?INTO?licence?(licence_id,?organization_id,?licence_type,?product_name,?licence_max,?licence_allocated)
VALUES?(3,?2,?'user','HR-PowerSuite',?100,4);
INSERT?INTO?licence?(licence_id,?organization_id,?licence_type,?product_name,?licence_max,?licence_allocated)
VALUES?(4,?2,?'core-prod','WildCat?Application?Gateway',?16,16);
--?organizations
INSERT?INTO?organization?(id,?name,?contact_name,?contact_email,?contact_phone)
VALUES?(1,?'customer-crm-co',?'Mark?Balster',?'[email protected]',?'823-555-1212');
INSERT?INTO?organization?(id,?name,?contact_name,?contact_email,?contact_phone)
VALUES?(2,?'HR-PowerSuite',?'Doug?Drewry','[email protected]',?'920-555-1212');
開始驗證
| 捕獲自定義異常
licenceId=1






| 捕獲進入 Controller 前的異常













| 捕獲未知異常



小結(jié)
擴展


總結(jié)

@Slf4j
@RestControllerAdvice
public?class?GlobalExceptionHandler?{
????/**
?????*?沒有登錄
?????*?@param?request
?????*?@param?response
?????*?@param?e
?????*?@return
?????*/
????@ExceptionHandler(NoLoginException.class)
????public?Object?noLoginExceptionHandler(HttpServletRequest?request,HttpServletResponse?response,Exception?e)
????{
????????log.error("[GlobalExceptionHandler][noLoginExceptionHandler]?exception",e);
????????JsonResult?jsonResult?=?new?JsonResult();
????????jsonResult.setCode(JsonResultCode.NO_LOGIN);
????????jsonResult.setMessage("用戶登錄失效或者登錄超時,請先登錄");
????????return?jsonResult;
????}
????/**
?????*?業(yè)務(wù)異常
?????*?@param?request
?????*?@param?response
?????*?@param?e
?????*?@return
?????*/
????@ExceptionHandler(ServiceException.class)
????public?Object?businessExceptionHandler(HttpServletRequest?request,HttpServletResponse?response,Exception?e)
????{
????????log.error("[GlobalExceptionHandler][businessExceptionHandler]?exception",e);
????????JsonResult?jsonResult?=?new?JsonResult();
????????jsonResult.setCode(JsonResultCode.FAILURE);
????????jsonResult.setMessage("業(yè)務(wù)異常,請聯(lián)系管理員");
????????return?jsonResult;
????}
????/**
?????*?全局異常處理
?????*?@param?request
?????*?@param?response
?????*?@param?e
?????*?@return
?????*/
????@ExceptionHandler(Exception.class)
????public?Object?exceptionHandler(HttpServletRequest?request,HttpServletResponse?response,Exception?e)
????{
????????log.error("[GlobalExceptionHandler][exceptionHandler]?exception",e);
????????JsonResult?jsonResult?=?new?JsonResult();
????????jsonResult.setCode(JsonResultCode.FAILURE);
????????jsonResult.setMessage("系統(tǒng)錯誤,請聯(lián)系管理員");
????????return?jsonResult;
????}
}
往期熱門文章:
1、監(jiān)控員工離職傾向系統(tǒng)已被下架,網(wǎng)友:勸你善良 2、同事說,我寫Java代碼像寫詩 3、阿里p7和副處級干部選哪個? 4、2021年互聯(lián)網(wǎng)公司“死亡”名單!2022 年跳槽一定要謹慎些! 5、京東程序員離職怒刪代碼被判10個月,京東到家請人花三萬恢復(fù)! 6、AlphaCode 驚世登場!編程版“阿法狗”悄悄參賽,擊敗一半程序員 7、被阿里P8面了兩個小時,技術(shù)、業(yè)務(wù)有來有回...... 8、員工春節(jié)加班猝死!反轉(zhuǎn)了,B站深夜發(fā)長文回應(yīng)! 9、字節(jié)跳動P0級事故:實習(xí)生刪除GB以下所有模型,直接上了今日頭條...... 10、1 個月崩 3 次!盤點一下 2021 年的 10 個宕機名場面
評論
圖片
表情
