发现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的源码了,有清楚原由的朋友也欢迎在评论区留言讨论!

你可能感兴趣的:(springCloud,spring)