今天無意中發現一個spring-cloud-starter-feign的一個問題
版本:
<spring-boot.version>1.5.15.RELEASE</spring-boot.version>
<spring-cloud.version>Edgware.SR4</spring-cloud.version>
先貼上代碼
服務A(調用方)
feignService
@FeignClient(name = "XXX-appuser-service", fallback = AppUserFeignFallbackServiceImpl.class)
public interface AppUserFeignService {
/**
* @Author chenqi
* @Description 根據用戶名查詢用戶信息
* @Param [userName]
**/
@GetMapping(value = "/feign/getUserInfByUserName")
AppUserVO getUserInfByUserName(@RequestParam(value = "userName") String userName);
}
feignFallbackServiceImpl
@Service
@Slf4j
public class AppUserFeignFallbackServiceImpl implements AppUserFeignService {
@Override
public AppUserVO getUserInfByUserName(String userName) {
log.error("調用【會員服務】執行{}方法異常 userName:{}", "getUserInfByUserName",
userName);
return null;
}
}
如果調用服務B異常,也返回null
service層調用代碼片段
@Override
public LoginVO login(LoginDTO dto) {
//查詢用戶信息
AppUserVO appUserVO = null;
try {
appUserVO = appUserFeignService.getUserInfByUserName(dto.getUserName());
} catch (AppUserServiceException e) {
e.printStackTrace();
throw new SrmServiceException(SrmErrorEnum.SRM_SERVICE_ERROR_201.getMsg() + ",失敗原因[" + e.getMessage() + "]");
} catch (Exception e) {
e.printStackTrace();
throw new SrmServiceException(SrmErrorEnum.SRM_SERVICE_ERROR_203.getMsg());
}
if(CheckUtils.objCheckNull(appUserVO)){//判斷用戶對象是否爲空
throw new SrmServiceException(SrmErrorEnum.SRM_SERVICE_ERROR_214.getMsg());
}else if(appUserVO.getFlagDel().intValue() == SrmStateConstant.SUPPLIER_ENABLED_1){//判斷用戶狀態
throw new SrmServiceException(SrmErrorEnum.SRM_SERVICE_ERROR_215.getMsg());
}
......
}
appUserVO.getFlagDel():該屬性不會爲null,數據表有默認值且設置了非空
AppUserServiceException :被調方服務B的自定義異常類,該類繼承RuntimeException
SrmServiceException:調用方服務A的自定義異常類,該類繼承RuntimeException
以上兩個自定義異常類均被全局異常類攔截
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {
/**
* 全局異常.
*
* @param e the e
* @return R
*/
@ExceptionHandler(Exception.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
@ResponseBody
public R exception(Exception e) {
log.info("保存全局異常信息 ex={}", e.getMessage(), e);
return new R<>(e);
}
/**
* 會員服務業務異常.
*
* @param e the e
* @return R
*/
@ExceptionHandler(AppUserServiceException.class)
@ResponseStatus(HttpStatus.OK)
@ResponseBody
public R exception(AppUserServiceException e) {
log.info("保存會員服務異常信息 ex={}", e.getMessage(), e);
return new R<>(e);
}
/**
* xxx服務業務異常.
*
* @param e the e
* @return R
*/
@ExceptionHandler(SrmServiceException.class)
@ResponseStatus(HttpStatus.OK)
@ResponseBody
public R exception(SrmServiceException e) {
log.info("保存XXX服務異常信息 ex={}", e.getMessage(), e);
return new R<>(e);
}
}
異常返回R類對異常的處理時,返回data爲null
服務B(被調用方)
Controller接口
/**
* @Author chenqi
* @Description 根據用戶名查詢用戶信息
* @Param [userName]
**/
@GetMapping(value = "/getUserInfByUserName")
public AppUserInfo getUserInfByUserName(@RequestParam(value = "userName") String userName){
return appUserInfoService.getByUserName(userName);
}
service
/**
* @Author chenqi
* @Description 根據用戶名查詢
* @Param [userName]
**/
AppUserInfo getByUserName(String userName);
serviceImpl
/**
* @Author chenqi
* @Description 根據用戶名查詢
* @Param [userName]
**/
@Override
public AppUserInfo getByUserName(String userName){
EntityWrapper wrapper = new EntityWrapper();
wrapper.eq(CommonConstant.FLAG_DEL, CommonConstant.STATUS_NORMAL);
wrapper.eq(AppUserCommonConstant.USER_NAME, userName);
AppUserInfo appUserInfo = selectOne(wrapper);
if (CheckUtils.objCheckNull(appUserInfo)) {//查詢用戶爲null,則拋出自定義異常
throw new AppUserServiceException(AppUserErrorEnum.APP_SERVICE_ERROR_210.getMsg());
}
return appUserInfo;
}
出現的問題
簡單描述下問題:
服務A 調用 服務B 的查詢用戶對象接口,服務B的該接口返回一個用戶對象,但是如果查詢對象爲空,則拋出自定義運行時異常。
問題:
由於服務B拋出了異常後,服務A接收到的返回結果就成了一個空對象,而不是null了,這點讓我很是費解…所以可想而知,服務A最終出現了NPE(空指針),也就是下面代碼中的else if裏
if(CheckUtils.objCheckNull(appUserVO)){//判斷用戶對象是否爲空
throw new SrmServiceException(SrmErrorEnum.SRM_SERVICE_ERROR_214.getMsg());
}else if(appUserVO.getFlagDel().intValue() == SrmStateConstant.SUPPLIER_ENABLED_1){//判斷用戶狀態
throw new SrmServiceException(SrmErrorEnum.SRM_SERVICE_ERROR_215.getMsg());
}
ps:
1、嘗試過修改服務B的代碼,查詢出對象爲null的話,就直接返回,不拋異常,這樣服務A接收到的纔是nulll
2、也嘗試過把服務B的異常改爲直接throw new RuntimeException(),這樣服務A接收到的也是null
但是由於其他因素,服務B查詢出對象爲null時,需要拋出自定義異常,所以最終選擇更改服務A的代碼
即在服務A的service層如下代碼的if判斷中額外增加一個對象屬性的非空判斷
if(CheckUtils.objCheckNull(appUserVO) || CheckUtils.longCheckNull(appUserVO.getUserId())){
throw new SrmServiceException(SrmErrorEnum.SRM_SERVICE_ERROR_214.getMsg());
}else if(appUserVO.getFlagDel().intValue() == SrmStateConstant.SUPPLIER_ENABLED_1){
throw new SrmServiceException(SrmErrorEnum.SRM_SERVICE_ERROR_215.getMsg());
}
但是最終也沒有明白,爲啥被調方服務B拋出自定義異常後,服務A接收到的就成了空對象了。
只能後面有時間再去研究下feign的源碼了,有清楚原由的朋友也歡迎在評論區留言討論!