發現spring-cloud-starter-feign的一個坑

今天無意中發現一個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的源碼了,有清楚原由的朋友也歡迎在評論區留言討論!

發佈了54 篇原創文章 · 獲贊 23 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章