统一异常处理

异常定义

自定义业务异常需继承 org.maculaframework.boot.core.exception.MaculaException 异常基类,定义如下

public abstract class MaculaException extends I18nException {
  private static final long serialVersionUID = 1L;

  public MaculaException(String message) {
    super(message);
  }
  public MaculaException(String message, Throwable cause) {
    super(message, cause);
  }
  public MaculaException(String message, Object[] args) {
    super(message, args);
  }
  public MaculaException(String message, Object[] args, Throwable cause) {
    super(message, args, cause);
  }
  public String getFullStackMessage() {
    StringWriter sw = new StringWriter();
    PrintWriter pw = new PrintWriter(sw, true);
    this.printStackTrace(pw);
    return sw.getBuffer().toString();
  }
}

XxxBizException异常示例

public class XxxBizException extends MaculaException {
   private static final long serialVersionUID = 1L;
   public XxxBizException(String message, Throwable cause) {
      super(message, cause);
   }
   public XxxBizException(String message, Object[] args) {
      super(message, args);
   }
   public XxxBizException(String message, Object[] args, Throwable cause) {
      super(message, args, cause);
   }
}

重要!!!

如无必要,不需要自己try异常,交由框架统一拦截处理,除非是你主动抛出业务类异常,或者捕获异常后有相应处理逻辑。特别提醒,如果在事务中,Service方法中并不能捕获到数据库类型的异常,因为事务结束后才会提交数据库,这个时候抛出的异常Service方法是捕获不到的

异常处理方式

框架内部自动异常处理: service 层异常处理 controller 层异常处理 orderedExceptionNegotiateFilter 过滤器异常处理

Service层异常处理

框架内部基于Spring AOP 特性, 用 @Service 注解构建切入点 , 针对所有service层横切 统一拦截处理service层抛出的异常。 Macula提供了 ErrorMessage 注解, 在service层 用于自定义方法级别异常信息输出, service层异常处理时,将异常转换为MaculaException 向上抛出

@AfterThrowing(pointcut = "service()", throwing = "ex")
public void doAfterThrowing(JoinPoint joinPoint, Throwable ex) {
    if (!(ex instanceof MaculaException)) {
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        Method method = methodSignature.getMethod();
        try {
            method = joinPoint.getTarget().getClass().getMethod(method.getName(), method.getParameterTypes());
        } catch (Exception e) {}

        ErrorMessage errorMessage = method.getAnnotation(ErrorMessage.class);
        String message = errorMessage == null ? "org.macula.boot.core.exception.ServiceException" : errorMessage.value();
        log.error(message, ex);
        throw translate(message, ex);
    }
}

Controller 层异常处理

框架内部基于spring mvc 的 @ControllerAdvice 和 @ExceptionHandler 注解组合实现全局 controller层异常拦截处理, 并统一用 Response 类封装异常信息返回

@ControllerAdvice
public class ControllerExceptionHandler {
    @ExceptionHandler(Exception.class)
    @ResponseBody
    public Response handlerCoreException(Exception ex, HttpServletRequest req) {
        if (ex instanceof MaculaException) {
            return new Response((MaculaException)ex);
        }
        if (ex instanceof IllegalArgumentException) {
            return new Response(new MaculaArgumentException((IllegalArgumentException)ex));
        }
        if (ex.getClass().equals("org.apache.dubbo.rpc.RpcException")) {
            return new Response(new ServiceException(MaculaConstants.EXCEPTION_CODE_RPC, "org.apache.dubbo.rpc.RpcException", ex));
        }
        ServiceException sex = new ServiceException(MaculaConstants.EXCEPTION_CODE_UNKNOWN, "org.maculaframework.boot.core.exception.ServiceException", ex);
        return new Response(sex);
    }
}

Response****响应示例

{
    "success":false,
    "errCode":"500",
    "errDesc":"获取数据失败",
    "exceptionStack":null,
    "redirection":null,
    "validateErrors":null
}

ErrorMessage注解

在你的Service方法中添加@ErrorMessage注解可以定制该方法出现异常时返回的错误信息,否则会统一返回“服务层异常”的消息提示

使用示例

@ErrorMessage(value = "根据集群name查找失败 ")
public GatewayClusterEntity findByClusterName(String name) {
  return gatewayClusterRepository.findByClusterName(name);
}

系统级异常处理

框架内部 基于Spring 框架的 OncePerRequestFilter 构建 OrderedExceptionNegotiateFilter 过滤器, 过滤每一个请求, 并根据请求特性,重新包装返回的异常信息,Controller层如果没有拦截到异常,会全部由 OrderedExceptionNegotiateFilter 接管处理 所有异常会统一用Response类封装,此时客户端收到的是HTTP 500的响应

Response****响应示例

{
    "success":false,
    "errCode":"500",
    "errDesc":null,
    "exceptionStack":null,
    "redirection":null,
    "validateErrors":null
}