前言
最近做一个spring微服务,当请求进入接口后,一旦发生错误,服务就会抛出异常,但是这个异常不是我们封装的,有时候跑出的信息我们想统一处理。
例:如果是客户请求数据错误应该报出400的HttpStatus.BAD_REQUEST。但是由于这个错误是我们内部抛出的他还是会自动抛500,而且服务有很多接口,我们管理也不方便。所以需要一个全局的异常处理。
1. 全局异常设置
我们这里设置两种异常,一种是客户请求的问题我们将他全都归为ClientRequestException 异常,我们服务内部的异常我们全都将它归为服务自身异常MyServiceRuntimeException。
1.1. 自定义客户请求异常
如请求数据错误。
import lombok.Data;
@Data
public class ClientRequestException extends RuntimeException {
private ErrorCode errorCode;
public ClientRequestException(ErrorCode errorCode, String appendMsg){
super(errorCode.getMsg()+" : "+appendMsg);
this.errorCode = errorCode;
}
}
1.2. 定义服务端内部异常
如JDBC链接超时。
import lombok.Data;
@Data
public class MyServiceRuntimeException extends RuntimeException {
private ErrorCode errorCode;
public MyServiceRuntimeException(ErrorCode errorCode, String appendMsg){
super(errorCode.getMsg()+" : "+appendMsg);
this.errorCode = errorCode;
}
}
1.3. 定义error code
定义自定义的统一管理的细致的error code。
public enum ErrorCode {
SYSTEM_ERROR("500-001","System error"),
SYSTEM_JDBC_CONNECTION_ERROR("500-002","System JDBC connection error"),
CLIENT_REQUEST_DATA_ERROR("400-001","request Data error");
private String code;
private String msg;
ErrorCode(String code,String msg){
this.code = code;
this.msg = msg;
}
public String getCode(){
return code;
}
public String getMsg(){
return msg;
}
}
1.5.定义全局异常处理类
如下需要用到 @RestControllerAdvice 和 @ExceptionHandler注解分别标注,和写明不同异常的处理方法和报错的归类,
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(MyServiceRuntimeException.class)
public ResponseEntity<ResponseException> myServiceRuntimeException(MyServiceRuntimeException ex){
ErrorCode errorCode = ex.getErrorCode();
ResponseException responseException = new ResponseException(errorCode.getCode(),ex.getMessage());
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(responseException);
}
@ExceptionHandler(ClientRequestException.class)
public ResponseEntity<ResponseException> myClientRuntimeException(ClientRequestException ex){
ErrorCode errorCode = ex.getErrorCode();
ResponseException responseException = new ResponseException(errorCode.getCode(),ex.getMessage());
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(responseException);
}
}
1.6. 自定义注解和AOP
定义AOP用于注解哪些类需要相同的异常处理。 这里仅定义给可能发生JDBC链接异常的所有API使用。客户端数据异常不使用注解(因为只有一接口会有数据检查,没必要做一个切面给他)
package com.acom.springdemo.exception.aop;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CommonExceptionHandler {
}
//第二个class
import com.acom.springdemo.exception.ErrorCode;
import com.acom.springdemo.exception.MyServiceRuntimeException;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
import org.springframework.transaction.CannotCreateTransactionException;
@Aspect
@Component
public class CommonExceptionHandlerAspect {
@Around("@annotation(com.acom.springdemo.exception.aop.CommonExceptionHandler)")
public void commonExceptionHandle(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
try{
proceedingJoinPoint.proceed();
}catch (CannotCreateTransactionException ex){
throw new MyServiceRuntimeException(ErrorCode.SYSTEM_JDBC_CONNECTION_ERROR,ex.getCause().getMessage());
}
}
}
2.使用全局异常
2.1. 服务自身异常使用
如上由于我们对JDBC异常(commonExceptionHandle)新建了注解,所以我们只需要将这个注解加在我们想监控的类上就可以了。
当服务内部出现JDBC链接异常就会有如下异常流程
- 发生JDBC链接异常Spring框架内部抛出CannotCreateTransactionException
- 进入自定义注解@CommonExceptionHandler内部代码
- 注解内部代码捕获后在抛出自定义MyServiceRuntimeException异常
- MyServiceRuntimeException异常被全局异常统一处理后抛出
2.2. 客户请求数据异常
假如id==333L就表示数据不对。
服务抛出400异常
该异常流程:
- 自我的服务发现数据不符合规定,抛出自定 ClientRequestException异常
- ClientRequestException异常被全局异常捕获,并相应400和自定义的异常信息