背景:对于dubbo服务,我们在调用provider提供的服务时,因为要考虑到provider服务的异常,所以往往会在consumer代码中大量使用 try{}catch(){},对于consumer,大量的捕获会显的项目臃肿、代码不够优雅,所以我们可以在provider构建一个全局异常捕获,将异常信息包装成统一的RESTful风格的返回值。dubbo全局捕获的实现方式有很多种,这次采用切面的方式进行处理,直接上代码:
/*
* bq.com
* Copyright (C) 2018-2020 All Rights Reserved.
*/
package com.alijk.bqhospital.common.aspect;
import com.alijk.bqhospital.client.exception.BusinessException;
import com.alijk.bqhospital.client.exception.ExceptionEnum;
import com.alijk.bqhospital.client.vto.response.base.Response;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
/**
* 自定义dubbo异常处理
*
* @author liuyuan
* @version DubboExceptionAspect.java, v 0.1 2020-09-28 20:35
*/
@Component
@Aspect
@Slf4j
public class DubboExceptionAspect {
/**
* 返回值类型为Response的Service
*/
@Pointcut(value = "execution(public com.alijk.bqhospital.client.vto.response.base.* com.alijk.bqhospital.client.facade..*Facade*.*(..))")
private void servicePointcut() {
}
/**
* 任何持有@Transactional注解的方法
*/
@Pointcut(value = "@annotation(org.springframework.transaction.annotation.Transactional)")
private void transactionalPointcut() {
}
/**
* 异常处理切面
* 将异常包装为Response,避免dubbo进行包装
*
* @param pjp 处理点
* @return Object
*/
@Around("servicePointcut() && !transactionalPointcut()")
public Object doAround(ProceedingJoinPoint pjp) {
return this.processException(pjp);
}
/**
* 任何持有@Transactional注解的方法异常处理切面
* 将自定义的业务异常转为RuntimeException:
* 1.规避dubbo的包装,让customer可以正常获取message
* 2.抛出RuntimeException使事务可以正确回滚
* 其他异常不处理
*
* @param pjp 处理点
* @return Object
*/
@Around("servicePointcut() && transactionalPointcut()")
public Object doTransactionalAround(ProceedingJoinPoint pjp) {
return this.processException(pjp);
}
/**
* 处理异常
*
* @param pjp 切点
* @return
*/
private Object processException(ProceedingJoinPoint pjp) {
Object[] args = pjp.getArgs();
try {
return pjp.proceed();
} catch (BusinessException e) { // 自定义异常
printlnException(pjp, args, e);
return Response.error(e.getCode(), e.getMsg());
} catch (Throwable e) {
printlnException(pjp, args, e);
return Response.error(ExceptionEnum.SYSTEM_ERROR);
}
}
/**
* 异常信息打印
*
* @param joinPoint 切点
* @param args 参数
* @param throwable 异常
*/
private void printlnException(final ProceedingJoinPoint joinPoint, final Object[] args, Throwable throwable) {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
String[] classNameArray = method.getDeclaringClass().getName().split("\\.");
String methodName = classNameArray[classNameArray.length - 1] + " >>> " + method.getName();
String inputParam = "";
if (args != null && args.length > 0) {
StringBuilder sb = new StringBuilder();
for (Object arg : args) {
sb.append(",");
sb.append(arg);
}
inputParam = sb.toString().substring(1);
}
log.warn("\n 方法: {}\n 入参: {} \n 异常: {}", methodName, inputParam, throwable.getMessage());
}
}