Dubbo丟擲自定義異常
阿新 • • 發佈:2019-01-27
最近開始學習Dubbo框架,在工作中會把之前的業務遷移過來。
在原來的Spring MVC框架實現中,有使用到自定義異常的場景(自定義異常繼承RuntimeException)。而對於異常(包括自定義異常),在業務程式碼中都不做任何try-catch操作,而是由公用的Controller來處理異常。
在使用dubbo的過程中,在dubbo的service端定義有自定義異常進行throw的時候,卻發現在customer的Controller中無法instanceof,自己自定義的異常類被轉成了Runtime異常,有點不理解。在翻看dubbo原始碼的時候,發現確實如此:
所以在dubbo的service端想丟擲自定義異常,只能通過在service端的介面方法上宣告所要丟擲的異常,或者將異常類與介面同包,再或者是介面的實現類再實現dubbo的GenericService介面。public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException { try { Result result = invoker.invoke(invocation); if (result.hasException() && GenericService.class != invoker.getInterface()) { try { Throwable exception = result.getException(); // 如果是checked異常,直接丟擲 if (! (exception instanceof RuntimeException) && (exception instanceof Exception)) { return result; } // 在方法簽名上有宣告,直接丟擲 try { Method method = invoker.getInterface().getMethod(invocation.getMethodName(), invocation.getParameterTypes()); Class<?>[] exceptionClassses = method.getExceptionTypes(); for (Class<?> exceptionClass : exceptionClassses) { if (exception.getClass().equals(exceptionClass)) { return result; } } } catch (NoSuchMethodException e) { return result; } // 未在方法簽名上定義的異常,在伺服器端列印ERROR日誌 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); // 異常類和介面類在同一jar包裡,直接丟擲 String serviceFile = ReflectUtils.getCodeBase(invoker.getInterface()); String exceptionFile = ReflectUtils.getCodeBase(exception.getClass()); if (serviceFile == null || exceptionFile == null || serviceFile.equals(exceptionFile)){ return result; } // 是JDK自帶的異常,直接丟擲 String className = exception.getClass().getName(); if (className.startsWith("java.") || className.startsWith("javax.")) { return result; } // 是Dubbo本身的異常,直接丟擲 if (exception instanceof RpcException) { return result; } // 否則,包裝成RuntimeException拋給客戶端 return new RpcResult(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); return result; } } return result; } catch (RuntimeException e) { 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); throw e; } }
對於第一種方案沒有使用,因為它對程式碼的入侵比較嚴重。
第二種方案可以實現,可對於目前的業務框架,讓介面類和異常類同包則變得不太可能。
所以最後選擇了讓介面實現類再實現GenericService介面,而對於其需要實現的$invoke方法則沒有做任何的方法體處理,直接廢棄。
對於dubbo的service端自定義異常類的處理,有些不理解的就是,為什麼dubbo需要對自定義異常類做一次Runtime異常的轉化,而不是直接丟擲原異常型別。或者有沒有對dubbo更瞭解的朋友,有對自定義異常更好的處理方法。