Java异常处理SOP

编程入门 行业动态 更新时间:2024-10-12 14:20:03

Java<a href=https://www.elefans.com/category/jswz/34/1771210.html style=异常处理SOP"/>

Java异常处理SOP

文章目录

  • 一、常见异常处理方式
  • 二.try catch
      • 1.catch类型
      • 2 插入数据的时候,需要更加精细的判断是为幂等冲突异常么
      • 3. catch的作用
      • 4.异常处理sop
      • 5、throw new Exception中添加信息
      • 5、捕获异常的粒度

一、常见异常处理方式

Service层func2,调用网关层func1

  • 直接吃掉

    public void func1() throws Exception1 {// ...
    }public void func2() {//...try {func1();} catch(Exception1 e) {log.warn("...", e); //吐掉:try-catch打印日志}//...
    }
    
  • 原封不动地re-throw

    public void func1() throws Exception1 {// ...
    }public void func2() throws Exception1 {//原封不动的re-throw Exception1//...func1();//...
    }
    
  • 包装成新的异常re-throw

    public void func1() throws Exception1 {// ...
    }public void func2() throws Exception2 {//...try {func1();} catch(Exception1 e) {throw new Exception2("...", e); // wrap成新的Exception2然后re-throw}//...
    }
    

当我们面对函数抛出异常的时候,应该选择上面的哪种处理方式呢?三个参考原则:

  • 如果func1()抛出的异常是可以恢复,且func2()的调用方并不关心此异常(弱依赖),我们完全可以在func2()内将func1()抛出的异常吞掉;
  • 如果func1()抛出的异常对func2()的调用方来说,强依赖,我们可以选择直接将func1抛出的异常re-throw;
  • 如果func1()抛出的异常太底层,对func2()的调用方来说,缺乏背景去理解、且业务概念上无关,我们可以将它重新包装成调用方可以理解的新异常,然后re-throw
  • 补充:网关层catch住异常的时候,可以不打error日志,因为很多网关层的方法都有重试3次,如果都是time out,则会打三个error信息,造成噪音干扰。方式一:网关层catch住异常,不打error只往上抛异常,调用方自己catch打异常信息。方式二:使用spring的retry注解,使用recover自定义方法,在原本网关方法内只抛不打error,在recover方法中打error,这样如果第一次、二次失败了,第三次成功了,则无error日志,如果三次全部失败,才有会打error日志
@Resource
private ProcesstService processtService;@Retryable(recover = "recoverSubmit")public String submit(BillSubmitRequest req) {try {BillSubmitResponse resp = processtService.submit(req);int code = resp.getErrorCode();if (code != 0) {throw new GatewayException(ExceptionCodeConstant.GATEWAY_EXCEPTION_CODE,"创建流程失败,resp:" + GsonUtils.toJsonStr(resp));}return resp.getBillNo();} catch (TException e) {throw new GatewayException(ExceptionCodeConstant.GATEWAY_EXCEPTION_CODE, "创建流程发生异常", e);}}@Recoverpublic String recoverSubmit(Exception e, BillSubmitRequest req) throws Exception {log.error("创建流程异常,req:[{}]", GsonUtils.toJsonStr(req), e);throw e;}

二.try catch

1.catch类型

  • 业务内部逻辑校验、处理等

    catch (BusinessException e) {log.warn("xxx-内部校验失败~ ", e);response.setCode(Constants.BUSINESS_ERROR);response.setMessage(e.getMessage());
    } 
    
  • rpc失败:超时、网络、对方服务等不知道的原因导致的失败,内部异常

    catch (Exception e) {log.error("xxx~ ", e);response.setCode(Constants.INTERNAL_ERROR);response.setMessage("系统异常");
    }
    
  • 一次普通的rpc异常判断

    try {} catch (TException e) {throw new BusinessException(500, true, e.getMessage());}
    

2 插入数据的时候,需要更加精细的判断是为幂等冲突异常么

Lists.partition(doList, batchThreshold).forEach(partitionData -> {try {this.xxxMapper.batchInsert(partitionData);} catch (Exception e) {if (e instanceof DuplicateKeyException) {log.warn("保存xxx数据重复,data:[{}]", GsonUtil.toJson(partitionData), e);} else {log.error("保存xxx数据发生异常,data:[{}]", GsonUtil.toJson(partitionData), e);}}});

3. catch的作用

  • 作用:是用来捕获异常的

  • 若先用Exception捕获,则后面的异常类型都不会走到,一旦发生了异常,直接被最大的Exception捕获了

  • 如果rpc调用结果code != 0 ,则说明rpc调用失败,此时如果后续逻辑强依赖这个rpc结果,而这个结果又没有查出来/错误的,就需要throw异常。如果不是强依赖,比如一品多码,即使错误了,后续流程仍可以正常走,则仅需要log.error打个错误日志即可

  • 一般使用gateway调用的rpc,catch的异常,要看调用的方法,抛出的是TException、还是Exception,对应的catch住,如果需要throw则抛Gateway_error

  • Business_error和interalError的区别:interan异常主要是一些无法预料的原因导致的rpc失败,比如网络抖动超时等。

  • catch匹配到异常后,会把异常吃掉。如果你在catch中打了相关信息,没有再向上抛出异常,则异常就在此处被吃掉了。如果是@Trasactional注解,异常就不能被吃掉,就需要在catch中再向上throw,这样事物才能一致。

  • 调用者为前端的时候,如果你不想让前端在调用时抛出红色异常。那么你就不在最外层catch中再次throw一个异常,而是吃掉这个异常,并且给出相应的code值和message即可。打出error日志即可

    前提是和前端约定好,不是0code则为异常,前端自己定义错误信息也可以

  • 如果你的接口是给别人调用的(前端、别人)你需要给出rpc异常的,比如TException

4.异常处理sop

1、不要忽略捕捉的异常

catch (NoSuchMethodException e) {return null;
}

虽然捕捉了异常但是却没有做任何处理,除非你确信这个异常可以忽略,不然不应该这样做。这样会导致外面无法知晓该方法发生了错误,无法确定定位错误原因。

2、在你的方法里抛出定义具体的检查性异常

public void foo() throws Exception { //错误方式
}
推荐:
public void foo() throws SpecificException1, SpecificException2 { //正确方式
}

3、捕获具体的子类而不是捕获 Exception 类

try {someMethod();
} catch (Exception e) { //错误方式LOGGER.error("method has failed", e);
}
推荐:
try {rpc();
} catch (TException e) { LOGGER.error("method has failed", e);
}

4、始终正确包装自定义异常中的异常,以便堆栈跟踪不会丢失

catch (NoSuchMethodException e) {throw new MyServiceException("Some information: " + e.getMessage());  //错误方式
}
推荐:
catch (NoSuchMethodException e) {throw new MyServiceException("Some information: " , e);  //正确方式
}

5、要么记录异常要么抛出异常,但不要一起执行

catch (NoSuchMethodException e) {  
//错误方式 LOGGER.error("Some information", e);throw e;
}

正如上面的代码中,记录和抛出异常会在日志文件中产生多条日志消息,代码中存在单个问题,并且对尝试分析日志的同事很不友好。

6、finally 块中永远不要抛出任何异常

7、始终只捕获实际可处理的异常

catch (NoSuchMethodException e) {throw e; //避免这种情况,因为它没有任何帮助
}

不要为了捕捉异常而捕捉,只有在想要处理异常时才捕捉异常,或者希望在该异常中提供其他上下文信息。如果你不能在 catch 块中处理它,那么最好的建议就是不要只为了重新抛出它而捕获它。

8、不要使用 printStackTrace() 语句或类似的方法

最终别人可能会得到这些堆栈,并且对于如何处理它完全没有任何方法,因为它不会附加任何上下文信息。

9、记住早 throw 晚 catch 原则

应该尽快抛出(throw)异常,并尽可能晚地捕获(catch)它。应该等到有足够的信息来妥善处理它。

10、在异常处理后清理资源

则仍应使用 try-finally 块来清理资源。 在 try 模块里面访问资源,在 finally 里面最后关闭资源。即使在访问资源时发生任何异常,资源也会优雅地关闭。

11、尽早验证用户输入以在请求处理的早期捕获异常

12、一个异常只能包含在一个日志中,在日志文件中这两个日志消息可能会间隔 100 多行。应该这样做:

LOGGER.debug("Using cache sector A");
LOGGER.debug("Using retry sector B");
推荐:
LOGGER.debug("Using cache sector A, using retry sector B");

13、编写多重catch语句块注意事项:顺序问题:先小后大,即先子类后父类

否则,捕获底层异常类的catch子句将可能会被屏蔽。

14、多层try、catch

throw后面的任何语句不被执行,最邻近的try块用来检查它是否含有一个与异常类型匹配的catch语句。如果发现了匹配的块,控制转向该语句;如果没有发现,次包围的try块来检查,以此类推。如果没有发现匹配的catch块,默认异常处理程序中断程序的执行并且打印堆栈轨迹。

5、throw new Exception中添加信息

throw new GatewayException(500,String.format("xxxx失败,req:[%s]", GsonUtils.toJsonStr(day+" "+id+" " +type)), e)

5、捕获异常的粒度

1、多仓,异常粒度要小到单仓

2、并发查询200、200,异常处理要小到每一次查询

 	public void test(){ExecutorService pool = Executors.newFixedThreadPool(3);List<Long> poiIdList = Lists.newArrayList(1L, 2L, 3L);List<String> result = poiIdList .stream().map(poiId -> CompletableFuture.supplyAsync(() -> {try {return queryPoiNameById(poiId);} catch (Exception e) {System.out.println(e);}return new ArrayList<String>();}, pool)).collect(Collectors.toList()).stream().map(CompletableFuture::join).flatMap(List::stream).collect(Collectors.toList());System.out.println(result);}public List<String> queryPoiNameById(Long poiId) {return Lists.newArrayList("-" + poiId);}

更多推荐

Java异常处理SOP

本文发布于:2024-03-12 23:28:58,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1732670.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:异常   Java   SOP

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!