### 一、自定义异常类
(1)继承 Exception 或 RuntimeException
(2)定义构造方法
```java
`/**
* @author lqh
* @date 2020/9/22
* 业务逻辑异常
*/
public class ServiceException extends RuntimeException{
private String code;
private String msg;
public ServiceException() {
}
public ServiceException(String msg) {
this.msg = msg;
}
public ServiceException(String code, String msg) {
this.code = code;
this.msg = msg;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
}`
```
### 二、@ControllerAdvice
@ControllerAdvice 实现全局异常处理,需配合@ExceptionHandler(注解用来指明异常的处理类型)使用。
```java
`/**
* @author lqh
*/
@ControllerAdvice
public class GlobalExceptionHandler {
@ResponseBody
@ExceptionHandler(Exception.class)
public ServerResponse exceptionHandler(Exception e) {
return ServerResponse.error(ResponseCode.SERVER_ERROR.getMsg());
}
@ResponseBody
@ExceptionHandler(ServiceException.class)
public ServerResponse serviceExceptionHandler(ServiceException se) {
return ServerResponse.error(se.getMsg());
}
}`
```
ResponseCode是自定义枚举类,用于返回统一的状态
```java
`/**
* @author lqh
* @date 2020/9/21
* 响应状态码
*/
public enum ResponseCode {
// 系统模块
SUCCESS(200, "操作成功"),
SAVE_SUCCESS(201,"保存成功"),
DELETE_SUCCESS(202,"删除成功!"),
UPDATE_SUCCESS(403,"更新成功!"),
ERROR(400, "操作失败"),
SAVE_ERROR(401,"保存失败"),
DELETE_ERROR(402,"删除失败!"),
UPDATE_ERROR(403,"更新成功"),
SERVER_ERROR(500, "服务器异常"),
EXCEPTION(-1,"Exception"),
// 用户模块 0xxxx
NEED_LOGIN(1001, "登录失效"),
USERNAME_OR_PASSWORD_EMPTY(1002, "用户名或密码不能为空"),
USERNAME_OR_PASSWORD_WRONG(1003, "用户名或密码错误"),
USER_NOT_EXISTS(1004, "用户不存在"),
WRONG_PASSWORD(1005, "密码错误"),
}`
```
### 四、异常e相关方法(解释在方法注释中)
外汇保证金计算https://www.fx61.com/cashdeposit.html
```java
`@Test
public void test01(){
try {
System.out.println(1/0);
}catch (Exception e){
/**
* 获取异常种类和错误信息
* java.lang.ArithmeticException: / by zero
*/
System.out.println(e.toString());
/**
*获取错误信息
* / by zero
*/
System.out.println(e.getMessage());
/**
* 获取异常类的Class
*class java.lang.ArithmeticException
*/
System.out.println(e.getClass());
/**
*获取异常类名称
*java.lang.ArithmeticException
*/
System.out.println(e.getClass().getName());
/**
* 会打出详细异常,异常名bai称,出错位置,便于调试用
* java.lang.ArithmeticException: / by zero
at com.bluewit.exception.ExceptionTest.test01(ExceptionTest.java:13)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
*/
e.printStackTrace();
/**
* 返回的是通过getOurStackTrace方法获取的StackTraceElement[]数组,而这个StackTraceElement是ERROR的每一个cause by的信息。
* [Ljava.lang.StackTraceElement;@4c203ea1
*/
System.out.println(e.getStackTrace());
/**
* 返回一个包含所有被抑制的异常,通常由一个数组try -with-resources语句,为了实现这一例外。 如果没有例外被抑制或抑制被禁止 ,则返回一个空数组
*/
System.out.println(e.getSuppressed());
/**
*回此异常的原因(尝试加载类时发生错误引发的异常;否则返回 null)
* null
*/
System.out.println(e.getCause());
}
}`
```
### 五、自定义异常使用
(1)举例一:程序中根据业务判断抛出异常信息
```java
`if(1!=1){
throw new ServiceException(ResponseCode.SUCCESS.getMsg());
}`
```
(2)举例二:根据异常信息抛出具体业务信息
```java
`try {
System.out.println(1/0);
}catch (Exception e){
if(e.toString().contains("SyntaxError")){
throw new ServiceException(ResponseCode.GRMMAR_RULES_ILLEGAL.getMsg());
}
}`
```
### 六、写在最后,使用异常处理业务,而不是处理业务逻辑
> 异常设计的初衷是解决程序运行中的各种意外情况,且异常的处理效率比条件判断方式要低很多。
看一个反例(使用异常处理业务逻辑)
```java
`public void processMessage(String token,RedisTemplate businessTemplate) {
try{
// 处理消息验证
// 处理消息解析
// 处理消息入库
}catch(ValidateException e ){
// 验证失败
}catch(ParseException e ){
// 解析失败
}catch(PersistException e ){
// 入库失败
}
}`
```
上面这个例子,很明显使用异常进行业务处理,这种方式是禁止使用的。
修改后的业务逻辑正例(使用异常处理业务)
```java
`public void processMessage(String token,RedisTemplate businessTemplate) {
if(StringUtils.isBlank(token)){
throw new ServiceException(ResponseCode.ILLEGAL_ARGUMENT.getMsg());
}
if(!RedisUtil.hasKey(token,businessTemplate)){
throw new ServiceException(ResponseCode.REPETITIVE_OPERATION.getMsg());
}
if(!RedisUtil.del(businessTemplate,token)){
throw new ServiceException(ResponseCode.REPETITIVE_OPERATION.getMsg());
}
}`
```
上面这个例子,只管抛出异常,不做任何处理,由GlobalExceptionHandler统一处理,代码变得简洁清晰,没有太多的代码嵌套,同时所有的异常都得到了妥善的处理。 |
|