一、try,catch中的嵌套使用分析
1.在被调用法的catch中不加throw进行异常抛出处理,(开发中遇到的bug:代码中出现sql新增报错,但是给前端返回的信息仍然是新增成功)代码如下:
public static void main(String[] args) {
try {
int num=1+3;
test();
System.out.println("num1的值如下:"+num);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static void test() {
try {
int num2=1/0;
System.out.println("num2:"+num2);
} catch (Exception e) {
log.info("错误信息:"+"除数为0");
//在不抛出被调用方使用了try,catch,但是没有在catch中使用throw抛出异常,
// 调用方虽然在调用一个方法上java语句上加上了try,catch,并在catch中异常抛出,
// 但是控制台打印的信息如下:
// 14:10:57.793 [main] INFO com.as.test.controller.ThridController - 错误信息:除数为0
// num1的值如下:4
//分析:并未达到,被调用方出错,调用法的程序到此为止的目的
// throw new RuntimeException(e);
}
}
运行结果如下:
14:22:58.922 [main] INFO com.as.test.controller.ThridController - 错误信息:除数为0
num1的值如下:4
分析:被调用方出现异常,程序并没有到此结束,而是继续执行调用方的后续代码,原因:如下,被调用方出现异常,使用catch进行了捕获,但是并没有对异常(抛出)进行处理,所以调用方无法在catch中进行异常捕获并抛出。
为了达到被调用方出现异常,程序到此结束,并在控制台抛出异常的代码修改如下:
public static void main(String[] args) {
try {
int num=1+3;
test();
System.out.println("num1的值如下:"+num);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static void test() {
try {
int num2=1/0;
System.out.println("num2:"+num2);
} catch (Exception e) {
log.info("错误信息:"+"除数为0");
throw new RuntimeException(e);
}
}
达到的目标效果如下:
14:19:59.073 [main] INFO com.as.test.controller.ThridController - 错误信息:除数为0
Exception in thread "main" java.lang.RuntimeException: java.lang.RuntimeException: java.lang.ArithmeticException: / by zero
at com.as.test.controller.ThridController.main(ThridController.java:71)
Caused by: java.lang.RuntimeException: java.lang.ArithmeticException: / by zero
at com.as.test.controller.ThridController.test(ThridController.java:87)
at com.as.test.controller.ThridController.main(ThridController.java:68)
Caused by: java.lang.ArithmeticException: / by zero
at com.as.test.controller.ThridController.test(ThridController.java:77)
... 1 more
1.2使用接口对try,catch中进行异常捕获,后续进行处理和不处理的接口测试如下: catch中对捕获异常,不进行处理
@GetMapping("/tryTest")
@ApiOperation("try,catch中进行异常捕获并抛出异常的结果测试")
public static PublicResult test() {
int num2=0;
try {
num2=1/0;
System.out.println("num2:"+num2);
} catch (Exception e) {
log.info("错误信息:"+"除数为0");
// throw new RuntimeException(e);
}
return new PublicResult("200","成功",num2);
}
swagger结果测试如下:
Response body
Download
{
"status": "200",
"msg": "成功",
"data": 0
}
catch中对捕获异常,进行处理
@GetMapping("/tryTest")
@ApiOperation("try,catch中进行异常捕获并抛出异常的结果测试")
public static PublicResult test() {
int num2=0;
try {
num2=1/0;
System.out.println("num2:"+num2);
} catch (Exception e) {
log.info("错误信息:"+"除数为0");
throw new RuntimeException(e);
}
return new PublicResult("200","成功",num2);
}
swagger结果测试如下:
{
"timestamp": "2024-07-27T06:41:03.950+0000",
"status": 500,
"error": "Internal Server Error",
"message": "java.lang.ArithmeticException: / by zero",
"path": "/tryTest"
try,catch也也可以在对数据库操作时,产生的异常进行捕获,可以抛出SQLExeption或者RuntimeException的异常 伪代码日如下:
try{
//调用对数据库中的数据操作的方法
dothing......
}catch(SQLException e){
throw new RuntimeException("运行时,发生的错误:"+e.getMessage());
}
1.3总结: 调用法和被调用方同时使用了try,catch都需要在catch捕获异常后,并对其进行处理,如异常抛出。
二、为什么使用全局异常?
如果我们不统一的处理异常,经常会在controller层有大量的异常处理的代码, 比如:
@Slf4j
@Api(value = "使用try,catch进行异常处理的演示")
@RestController
@RequestMapping("/user")
public class UserController {
@ApiOperation("/add")
@ApiImplicitParam(name = "user", type = "body", dataTypeClass = User.class, required = true)
@PostMapping("add")
public ResponseEntity<String> add(@Valid @RequestBody UseParam user) {
// 每个接口充斥着大量的异常处理
try {
// do something
} catch(Exception e) {
return ResponseEntity.fail("error");
}
return ResponseEntity.ok("success");
}
}
三、全局异常的实现如下:
1.定义结果状态的枚举
package com.gxa.shop.common.exception;
/**
* 结果状态的枚举
*/
public enum ResultCode {
/**
* 成功 200
*/
SUCCESS(0, "成功"),
SUCCESS_FIND(0, "查询成功"),
SUCCESS_CRUD(0, "更新成功"),
//操作失败failed
FAILED_CRUD(-1, "操作失败"),
/**
* 数据验证有关 -- 3000*
*/
UN_DATA(30001, "没有数据"),
PARAM_ERROR(30002, "参数不正确"),
PARAM_FORMAT_ERROR(30003, "参数数据格式有误"),
COMMENT_EMPTY(30004, "内容必须填写"),
CODE_ERROR(30005, "验证码错误"),
/**
* 手机号已被注册
*/
REGISTER_MOBILE_ERROR(30006, "手机号已被注册"),
/**
* 登录 -- code 4000*开始
*/
LOGIN_PHONE_ERROR(40001, "手机号码不正确"),
LOGIN_ACCOUNT_ERROR(40002, "账号不正确"),
/**
* 登陆失败
*/
LOGIN_ERROR(31003, "登录失败,用户名或者密码错误"),
LOGIN_PASSWORD_ERROR(40004, "密码不正确"),
/**
* 该用户已被禁用
*/
LOGIN_DISABLED_ERROR(40005, "该用户已被禁用"),
/**
* 登录相关 token
*/
TOKEN_EXPIRED_ERROR(40006, "token过期,请重新登录"),
TOKEN_NULL(40007, "token不能为空,请重新登录"),
/**
* 权限相关 4100*
*/
LOGIN_AUTH(41001, "需要登录"),
LOGIN_ACL(41002, "没有权限"),
LOGIN_ACCESS(41003, "权限问题"),
FETCH_ACCESSTOKEN_FAIL(41004, "获取accessToken失败"),
FETCH_USERINFO_ERROR(43004, "获取用户信息失败"),
/**
* 短信服务 1000
*/
SMS_SEND_ERROR(10001, "短信发送失败"),
SMS_SEND_ERROR_BUSINESS_LIMIT_CONTROL(10002, "短信发送过于频繁"),
/**
* 其他 错误信息 5000
*/
BAD_SQL_GRAMMAR(50001, "sql语法错误"),
JSON_PARSE_ERROR(50002, "json解析异常"),
URL_ENCODE_ERROR(50003, "URL编码失败"),
ILLEGAL_CALLBACK_REQUEST_ERROR(50004, "非法回调请求"),
GATEWAY_ERROR(50005, "服务不能访问"),
UNKNOWN_REASON(50006, "未知错误"),
RUNTIME_EXCEPTION(50007, "服务运行报错,请联系管理员处理"),
REQUEST_METHOD_ERROR(50008, "请求方式有误"),
STACK_OVERFLOW_ERROR(50009, "栈溢出"),
DATA_OCCUPIED_ERROR(50010, "数据被引用不能删除" ),
RESULT_ERROR(500011, "结果集设置异常"),
FILE_HEAD_ERROR(500012, "头部文件报错"),
FILE_CONTEXT_ERROR(500013, "头部内容出错"),
FILE_NOTHING_ERROR(500014, "文件内容为空"),
FILE_NULL_ERROR(500015, "文件为空"),
STOCKLT_ERROR(500016,"库存不足,不能下单");
private Integer code;
private String message;
ResultCode(Integer code, String message) {
this.code = code;
this.message = message;
}
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
2.自定义异常
package com.gxa.shop.common.exception;
import com.gxa.hualianeshop.common.base.ResultCode;
public class BizException extends RuntimeException{
private ResultCode resultCode;
public ResultCode getExceptionCode() {
return resultCode;
}
public BizException(ResultCode exceptionCode){
super(exceptionCode.getMessage());
this.resultCode = exceptionCode;
}
}
3.返回结果
package com.gxa.hualianeshop.common.base;
import lombok.Data;
import java.io.Serializable;
/**
* 通用的返回给前端的状态信息的工具类, 本身不需要注册bean
* @author bill
* @date 2023/6/2 16:37
*/
@Data
public class R implements Serializable {
private String code;
private String msg;
private Object data;
public R(String code, String msg) {
this.code = code;
this.msg = msg;
}
public R() {
}
public R(Object data) {
this.code = "200";
this.msg = "success";
this.data = data;
}
public static R ok(){
return new R("200","success");
}
public static R ok(Object data){
return new R(data);
}
public static R failed(){
return new R("500","faild");
}
public static R failed(String message){
return new R("500",message);
}
}
4.全局处理器的实现
package com.gxa.hualianeshop.common.handler;
import com.gxa.shop.common.base.R;
import com.gxa.shop.common.base.ResultCode;
import com.gxa.shop.common.exception.BizException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestController;
@RestController
//@ControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
/*
根据异常类型,选择不同的方法,ExceptionHandler的属性value
@handler(Exception.class)
public Map exceptionHandler(){
}*/
/**
* 也可以选择将所有异常集中处理
* 异常的信息是通过声明参数的形式,让SpringMVC自动注入
* @param e
* @return
*/
@ExceptionHandler(Throwable.class)
public R exceptionHandler(Exception e){
if(e instanceof BizException){
BizException be = ((BizException)e);
return R.failed(be.getExceptionCode().getMessage());
}else{
// 记录日志
log.error(e.toString());
return R.failed(ResultCode.RUNTIME_EXCEPTION.getMessage());
}
}
}
四、使用try,catch或者全局异常的一起使用对异常进行捕获并处理的顺序
try的优先级高于全局异常,并且尽量两者不要同时使用,可能会导致一些异常捕获,达不到预期的效果