今天无意中发现一个spring-cloud-starter-feign的一个问题
版本:
<spring-boot.version>1.5.15.RELEASE</spring-boot.version>
<spring-cloud.version>Edgware.SR4</spring-cloud.version>
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
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的源码了,有清楚原由的朋友也欢迎在评论区留言讨论!