项目场景:
dubbo服务提供端抛出自定义异常BusinessException,消费端捕获后统一封装处理数据。
问题描述:
消费端捕获的异常类却为RuntimeException,根据打印异常信息,发现dubbo服务端自定义异常被封装成RuntimeException后抛出,如下所示:
java.lang.RuntimeException: com.xiaozhou.common.domain.BusinessException: 对不起,您没有权限访问
com.xiaozhou.common.domain.BusinessException: 对不起,您没有权限访问
at com.xiaozhou.dynamic.api.ArticleApiImpl.queryArticleDtoByAuth(ArticleApiImpl.java:115)
at org.apache.dubbo.common.bytecode.Wrapper2.invokeMethod(Wrapper2.java)
at org.apache.dubbo.rpc.proxy.javassist.JavassistProxyFactory$1.doInvoke(JavassistProxyFactory.java:47)
at org.apache.dubbo.rpc.proxy.AbstractProxyInvoker.invoke(AbstractProxyInvoker.java:84)
at org.apache.dubbo.config.invoker.DelegateProviderMetaDataInvoker.invoke(DelegateProviderMetaDataInvoker.java:56)
at org.apache.dubbo.rpc.protocol.InvokerWrapper.invoke(InvokerWrapper.java:56)
at org.apache.dubbo.rpc.filter.ExceptionFilter.invoke(ExceptionFilter.java:55)
at org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:82)
at org.apache.dubbo.monitor.support.MonitorFilter.invoke(MonitorFilter.java:92)
at org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:82)
at org.apache.dubbo.rpc.filter.TimeoutFilter.invoke(TimeoutFilter.java:48)
at org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:82)
at org.apache.dubbo.rpc.protocol.dubbo.filter.TraceFilter.invoke(TraceFilter.java:81)
at org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:82)
at com.xiaozhou.dynamic.filter.ExceptionFilter.invoke(ExceptionFilter.java:39)
at org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:82)
at org.apache.dubbo.rpc.filter.ContextFilter.invoke(ContextFilter.java:96)
at org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:82)
at org.apache.dubbo.rpc.filter.GenericFilter.invoke(GenericFilter.java:148)
at org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:82)
at org.apache.dubbo.rpc.filter.ClassLoaderFilter.invoke(ClassLoaderFilter.java:38)
at org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:82)
at org.apache.dubbo.rpc.filter.EchoFilter.invoke(EchoFilter.java:41)
at org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:82)
at org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper$CallbackRegistrationInvoker.invoke(ProtocolFilterWrapper.java:157)
at org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol$1.reply(DubboProtocol.java:152)
at org.apache.dubbo.remoting.exchange.support.header.HeaderExchangeHandler.handleRequest(HeaderExchangeHandler.java:102)
at org.apache.dubbo.remoting.exchange.support.header.HeaderExchangeHandler.received(HeaderExchangeHandler.java:193)
at org.apache.dubbo.remoting.transport.DecodeHandler.received(DecodeHandler.java:51)
at org.apache.dubbo.remoting.transport.dispatcher.ChannelEventRunnable.run(ChannelEventRunnable.java:57)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
at org.apache.dubbo.rpc.filter.ExceptionFilter$ExceptionListener.onResponse(ExceptionFilter.java:105) ~[dubbo-2.7.3.jar:2.7.3]
at org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper$CallbackRegistrationInvoker.lambda$invoke$0(ProtocolFilterWrapper.java:167) ~[dubbo-2.7.3.jar:2.7.3]
at org.apache.dubbo.rpc.AsyncRpcResult.lambda$whenCompleteWithContext$0(AsyncRpcResult.java:155) ~[dubbo-2.7.3.jar:2.7.3]
at java.util.concurrent.CompletableFuture.uniWhenComplete(CompletableFuture.java:760) ~[na:1.8.0_202]
at java.util.concurrent.CompletableFuture.uniWhenCompleteStage(CompletableFuture.java:778) ~[na:1.8.0_202]
at java.util.concurrent.CompletableFuture.whenComplete(CompletableFuture.java:2140) ~[na:1.8.0_202]
at org.apache.dubbo.rpc.AsyncRpcResult.whenCompleteWithContext(AsyncRpcResult.java:153) ~[dubbo-2.7.3.jar:2.7.3]
at org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper$CallbackRegistrationInvoker.invoke(ProtocolFilterWrapper.java:159) ~[dubbo-2.7.3.jar:2.7.3]
at org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol$1.reply(DubboProtocol.java:152) ~[dubbo-2.7.3.jar:2.7.3]
at org.apache.dubbo.remoting.exchange.support.header.HeaderExchangeHandler.handleRequest(HeaderExchangeHandler.java:102) ~[dubbo-2.7.3.jar:2.7.3]
at org.apache.dubbo.remoting.exchange.support.header.HeaderExchangeHandler.received(HeaderExchangeHandler.java:193) ~[dubbo-2.7.3.jar:2.7.3]
at org.apache.dubbo.remoting.transport.DecodeHandler.received(DecodeHandler.java:51) ~[dubbo-2.7.3.jar:2.7.3]
at org.apache.dubbo.remoting.transport.dispatcher.ChannelEventRunnable.run(ChannelEventRunnable.java:57) ~[dubbo-2.7.3.jar:2.7.3]
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) ~[na:1.8.0_202]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) ~[na:1.8.0_202]
at java.lang.Thread.run(Thread.java:748) ~[na:1.8.0_202]
原因分析:
一、查看服务端控制台输出错误日志如下:
ERROR 8216 — [20001-thread-16] .d.r.f.ExceptionFilter$ExceptionListener : [DUBBO] Got unchecked and undeclared exception which called by 192.168.*.*. service: com.xiaozhou.dynamic.api.ArticleApi, method: queryArticleDtoByAuth, exception: com.xiaozhou.common.domain.BusinessException: 对不起,您没有权限访问, dubbo version: 2.7.3, current host: 192.168.*.*
二、查阅相关内容与源码分析
现象是dubbo的ExceptionFilter类将自定义异常封装成RuntimeException后抛出,上面消费端打印异常也可看出;然后下面为org.apache.dubbo.rpc.filter.ExceptionFilter中的onResponse源码分析结论。
1.异常类不是RuntimeException并且是Exception时,直接抛出
2.异常类是在方法签名上声明的抛出异常时,return 直接抛出
3.异常类和接口类在同一个jar包中时,直接抛出
4.异常类是如为"java."或"javax."开头的类名时(JDK的),直接抛出
5.异常类是dubbo自己的异常RpcException类时,直接抛出
6.其他,封装成RuntimeException类抛出
解决方案:
根据以上的原因可以对应方案:
1.自定义异常类改成Exception继承类,启动checked不通过,不太现实
2.在对应的服务接口方法上直接throw抛出自定义异常
3.将自定义异常与接口类放置同一个jar包中
4.重写dubbo的org.apache.dubbo.rpc.filter.ExceptionFilter,在dubbo服务端中如下操作:
- 将ExceptionFilter类源码copy出来,自定义类
- 在项目中资源resources目录下新建META-INF目录,再新建的目录中再新建dubbo目录,然后在此dubbo目录中新建个com.alibaba.dubbo.rpc.Filter的文件,文件中增加一行自定义类路径,如下:
- 在springboot的配置文件中新加dubbo.provider.filter配置,配置为:customExceptionFilter,-exception
- 最后就是进行自定义类改造了,如下:
(1)直接判断如果为自定义异常类,直接return抛出
(2)直接判断如果为自定义异常类的类路径,直接return抛出