dubbo服务提供者无法捕捉自定义异常的问题解决
1254 字
6 分钟
dubbo服务提供者无法捕捉自定义异常的问题解决
一、问题
Dubbo服务调用过程中抛出的自定义异常捕获不到,总是抛出了一个RuntimeException包装了自定义异常,导致全局异常处理器捕捉不到自定义异常。
经过断点可知:

自定义异常确实被包装成了RuntimeException异常。
经过断点调试跳转到ExceptionFilter这个类,在org.apache.dubbo.rpc.filter下,贴出源码 版本2
7.8
package org.apache.dubbo.rpc.filter;
import org.apache.dubbo.common.constants.CommonConstants;import org.apache.dubbo.common.extension.Activate;import org.apache.dubbo.common.logger.Logger;import org.apache.dubbo.common.logger.LoggerFactory;import org.apache.dubbo.common.utils.ReflectUtils;import org.apache.dubbo.common.utils.StringUtils;import org.apache.dubbo.rpc.Filter;import org.apache.dubbo.rpc.Invocation;import org.apache.dubbo.rpc.Invoker;import org.apache.dubbo.rpc.Result;import org.apache.dubbo.rpc.RpcContext;import org.apache.dubbo.rpc.RpcException;import org.apache.dubbo.rpc.service.GenericService;
import java.lang.reflect.Method;
/** * ExceptionInvokerFilter * <p> * Functions: * <ol> * <li>unexpected exception will be logged in ERROR level on provider side. Unexpected exception are unchecked * exception not declared on the interface</li> * <li>Wrap the exception not introduced in API package into RuntimeException. Framework will serialize the outer exception but stringnize its cause in order to avoid of possible serialization problem on client side</li> * </ol> */@Activate(group = CommonConstants.PROVIDER)public class ExceptionFilter implements Filter, Filter.Listener { private Logger logger = LoggerFactory.getLogger(ExceptionFilter.class);
@Override public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException { return invoker.invoke(invocation); }
@Override public void onResponse(Result appResponse, Invoker<?> invoker, Invocation invocation) { if (appResponse.hasException() && GenericService.class != invoker.getInterface()) { try { Throwable exception = appResponse.getException();
// directly throw if it's checked exception // 检查是否是RuntimeException或者Exception 我的自定义异常继承自RuntimeException 不满足此条件 if (!(exception instanceof RuntimeException) && (exception instanceof Exception)) { return; } // directly throw if the exception appears in the signature // 方法上也没有声明我的自定义异常 不满足此条件 try { Method method = invoker.getInterface().getMethod(invocation.getMethodName(), invocation.getParameterTypes()); Class<?>[] exceptionClassses = method.getExceptionTypes(); for (Class<?> exceptionClass : exceptionClassses) { if (exception.getClass().equals(exceptionClass)) { return; } } } catch (NoSuchMethodException e) { return; }
// for the exception not found in method's signature, print ERROR message in server's log. logger.error("Got unchecked and undeclared exception which called by " + RpcContext.getContext().getRemoteHost() + ". service: " + invoker.getInterface().getName() + ", method: " + invocation.getMethodName() + ", exception: " + exception.getClass().getName() + ": " + exception.getMessage(), exception);
// directly throw if exception class and interface class are in the same jar file. // 如果异常类和接口类在同一jar文件中,则直接抛出 接口和异常不在同一个jar包中 不满足 String serviceFile = ReflectUtils.getCodeBase(invoker.getInterface()); String exceptionFile = ReflectUtils.getCodeBase(exception.getClass()); if (serviceFile == null || exceptionFile == null || serviceFile.equals(exceptionFile)) { return; } // directly throw if it's JDK exception // 如果是JDK异常 直接抛出 我的是自定义异常 不满足 String className = exception.getClass().getName(); if (className.startsWith("java.") || className.startsWith("javax.")) { return; } // directly throw if it's dubbo exception // 如果是dubbo的异常 直接抛出 我的自定义异常 不满足 if (exception instanceof RpcException) { return; }
// otherwise, wrap with RuntimeException and throw back to the client // 这里就说明了,其它异常会包装成一个RuntimeException并返回给客户端 appResponse.setException(new RuntimeException(StringUtils.toString(exception))); } catch (Throwable e) { logger.warn("Fail to ExceptionFilter when called by " + RpcContext.getContext().getRemoteHost() + ". service: " + invoker.getInterface().getName() + ", method: " + invocation.getMethodName() + ", exception: " + e.getClass().getName() + ": " + e.getMessage(), e); } } }
@Override public void onError(Throwable e, Invoker<?> invoker, Invocation invocation) { logger.error("Got unchecked and undeclared exception which called by " + RpcContext.getContext().getRemoteHost() + ". service: " + invoker.getInterface().getName() + ", method: " + invocation.getMethodName() + ", exception: " + e.getClass().getName() + ": " + e.getMessage(), e); }
// For test purpose public void setLogger(Logger logger) { this.logger = logger; }}二、解决方案
- 在方法上声明自定义异常 也就是throws 自定义异常
- 将异常和接口放到同一个包下
- 重写一个ExceptionFilter替代dubbo的ExceptionFilter
这里说下方案3 代码就是复制的ExceptionFilter的代码 需要在里面把我们的自定义异常处理一下 顺便把otherwise下面的包装成我们的自定义异常,不让它再包装成RuntimeException
import com.tianch.platform.common.log.exception.PException;import com.tianch.platform.common.log.exception.enmus.PExceptionEnum;import lombok.extern.slf4j.Slf4j;import org.apache.dubbo.common.constants.CommonConstants;import org.apache.dubbo.common.extension.Activate;import org.apache.dubbo.common.utils.ReflectUtils;import org.apache.dubbo.rpc.*;import org.apache.dubbo.rpc.service.GenericService;
import java.lang.reflect.Method;
@Slf4j@Activate(group = CommonConstants.PROVIDER)public class DubboProviderFilter implements Filter, Filter.Listener {
@Override public Result invoke(Invoker<?> invoker, Invocation invocation) throws org.apache.dubbo.rpc.RpcException { return invoker.invoke(invocation); }
@Override public void onResponse(Result appResponse, Invoker<?> invoker, Invocation invocation) { if (appResponse.hasException() && GenericService.class != invoker.getInterface()) { try { Throwable exception = appResponse.getException();
// directly throw if it's checked exception if (!(exception instanceof RuntimeException) && (exception instanceof Exception)) { return; } // directly throw if the exception appears in the signature try { Method method = invoker.getInterface().getMethod(invocation.getMethodName(), invocation.getParameterTypes()); Class<?>[] exceptionClassses = method.getExceptionTypes(); for (Class<?> exceptionClass : exceptionClassses) { if (exception.getClass().equals(exceptionClass)) { return; } } } catch (NoSuchMethodException e) { return; }
// for the exception not found in method's signature, print ERROR message in server's log. log.error("Got unchecked and undeclared exception which called by " + org.apache.dubbo.rpc.RpcContext.getContext().getRemoteHost() + ". service: " + invoker.getInterface().getName() + ", method: " + invocation.getMethodName() + ", exception: " + exception.getClass().getName() + ": " + exception.getMessage(), exception);
// directly throw if exception class and interface class are in the same jar file. String serviceFile = ReflectUtils.getCodeBase(invoker.getInterface()); String exceptionFile = ReflectUtils.getCodeBase(exception.getClass()); if (serviceFile == null || exceptionFile == null || serviceFile.equals(exceptionFile)) { return; } // directly throw if it's JDK exception String className = exception.getClass().getName(); if (className.startsWith("java.") || className.startsWith("javax.")) { return; } // directly throw if it's dubbo exception if (exception instanceof RpcException) { return; }
// 如果是我们的自定义异常 if (exception instanceof PException) { return; }
// otherwise, 包装成我们的自定义异常 String application = invoker.getUrl().getParameter(CommonConstants.APPLICATION_KEY); PException pException = new PException(application, PExceptionEnum.SYSTEM_EXCEPTION.getCode(), exception.getMessage()); appResponse.setException(pException); } catch (Throwable e) { log.warn("Fail to ExceptionFilter when called by " + org.apache.dubbo.rpc.RpcContext.getContext().getRemoteHost() + ". service: " + invoker.getInterface().getName() + ", method: " + invocation.getMethodName() + ", exception: " + e.getClass().getName() + ": " + e.getMessage(), e); } } }
@Override public void onError(Throwable e, Invoker<?> invoker, Invocation invocation) { log.error("Got unchecked and undeclared exception which called by " + RpcContext.getContext().getRemoteHost() + ". service: " + invoker.getInterface().getName() + ", method: " + invocation.getMethodName() + ", exception: " + e.getClass().getName() + ": " + e.getMessage(), e); }
}和dubbo一样,利用SPI机制加载我们的异常过滤器

仿照这个文件写一个把我们自己的异常过滤器声明出来

在配置里去掉dubbo的异常过滤器并加上我们自己的异常过滤器

注意-是排除
文章分享
如果这篇文章对你有帮助,欢迎分享给更多人!
相关文章智能推荐
1
dubbo微服务之间的隐式数据传递
Java在做全链路压测插件时,如何让流量标识在dubbo远程RPC调用时进行传递是一个需要解决的问题,就可以用dubbo的Attachment做隐式传递,服务A) -- (服务B -- )(服务C)等等.
2
FRP v0.68.0 内网穿透完整教程
Nginx文档版本:v1.0 | 适用 FRP 版本:v0.68.0 | 更新日期:2026-07-31
3
SpringBoot统一参数校验
Java2026-01-05
4
JAVA设计模式的七大原则
其他2026-01-05
5
无法加载文件 xxx 因为在此系统上禁止运行脚本
其他2026-01-05
随机文章随机推荐


