根據面試經驗而言,能在簡歷上寫上原理、源碼等關鍵詞的,是很是具有核心競爭力的.上週和粉絲交流面試狀況以下java
若是想學習Java工程化、高性能及分佈式、深刻淺出。微服務、Spring,MyBatis,Netty源碼分析的朋友能夠加個人Java高級交流:854630135,羣裏有阿里大牛直播講解技術,以及Java大型互聯網技術的視頻免費分享給你們。面試
面試的時候,把源碼一波分析,令面試官虎軀一震!在一陣前戲事後,覺得接下來無非就是身體的一頓抽搐一切變得索然無味,不料面試官來了句令劇情發生了反轉api
"你對Dubbo源碼這麼熟悉,那請問你使用的時候,有沒有遇到什麼坑"服務器
我擦,毫無準備的他,菊花頓時一緊!此時就面臨唬住了50K,唬不住就只能5K的局面,慌了!微信
論如何反殺架構
相信你們面試都遇到過相似問題,由於源碼解析網上不少,不少人"考前突擊"一下,可是遇到喜歡問細節的面試官,終究難逃法眼,無處遁形.遇到這個問題,咱們如何反殺一波?那麼我就從一次聊天記錄提及,畢竟只有關注肥朝公衆號,擁有真實場景的源碼實戰(很是重要),遇到這類問題,纔不至於出現猛虎落淚的情形分佈式
真實場景描述ide
那麼咱們把業務相關去掉,抽取一個最簡模型.咱們在公司,通常都會有本身的自定義異常,而後這個自定義異常通常放在common.jar給其餘模塊依賴,好比我這裏定義一個HelloException微服務
1public class HelloException extends RuntimeException { 2 3 public HelloException() { 4 } 5 6 public HelloException(String message) { 7 super(message); 8 } 9 10}
而後咱們寫一個最簡單的Dubbo的demo,以下源碼分析
interface
1public interface DemoService { 2 3 String sayHello(String name); 4 5}
provider
1public class DemoServiceImpl implements DemoService { 2 3 public String sayHello(String name) { 4 throw new HelloException("公衆號:肥朝"); 5 } 6 7}
consumer
1public class DemoAction { 2 3 private DemoService demoService; 4 5 public void setDemoService(DemoService demoService) { 6 this.demoService = demoService; 7 } 8 9 public void start() throws Exception { 10 try { 11 String hello = demoService.sayHello("公衆號:肥朝"); 12 } catch (HelloException helloException) { 13 System.out.println("這裏捕獲helloException異常"); 14 } 15 } 16 17}
按照聊天記錄的描述,此時consumer調用provider,provider拋出HelloException.可是consumer捕獲到的,卻不是HelloException.
那麼咱們運行看看
果真如該同事所言.爲何會這樣呢?以前沒看過肥朝Dubbo源碼解析系列的同窗這種時候每每採用最低效的解決辦法,把異常棧往微信羣一丟,各類求助.可是每每毫無收穫,而後感嘆社會爲什麼如此冷漠!
可是相信公衆號的老粉絲們早已掌握閱讀源碼的技能,和肥朝同樣坐懷不亂,九淺一深直入源碼.出現異常咱們首先看一下異常棧
除非擼多了看不清(建議戒擼),不然這行異常和肥朝同樣,就像漆黑中的螢火蟲同樣,那麼鮮明,那麼出衆
1com.alibaba.dubbo.rpc.filter.ExceptionFilter.invoke(ExceptionFilter.java:108)
那麼咱們一探究竟
若是想學習Java工程化、高性能及分佈式、深刻淺出。微服務、Spring,MyBatis,Netty源碼分析的朋友能夠加個人Java高級交流:854630135,羣裏有阿里大牛直播講解技術,以及Java大型互聯網技術的視頻免費分享給你們。
1 public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException { 2 try { 3 Result result = invoker.invoke(invocation); 4 if (result.hasException() && GenericService.class != invoker.getInterface()) { 5 try { 6 Throwable exception = result.getException(); 7 8 // 若是是checked異常,直接拋出 9 if (! (exception instanceof RuntimeException) && (exception instanceof Exception)) { 10 return result; 11 } 12 // 在方法簽名上有聲明,直接拋出 13 try { 14 Method method = invoker.getInterface().getMethod(invocation.getMethodName(), invocation.getParameterTypes()); 15 Class<?>[] exceptionClassses = method.getExceptionTypes(); 16 for (Class<?> exceptionClass : exceptionClassses) { 17 if (exception.getClass().equals(exceptionClass)) { 18 return result; 19 } 20 } 21 } catch (NoSuchMethodException e) { 22 return result; 23 } 24 25 // 未在方法簽名上定義的異常,在服務器端打印ERROR日誌 26 logger.error("Got unchecked and undeclared exception which called by " + RpcContext.getContext().getRemoteHost() 27 + ". service: " + invoker.getInterface().getName() + ", method: " + invocation.getMethodName() 28 + ", exception: " + exception.getClass().getName() + ": " + exception.getMessage(), exception); 29 30 // 異常類和接口類在同一jar包裏,直接拋出 31 String serviceFile = ReflectUtils.getCodeBase(invoker.getInterface()); 32 String exceptionFile = ReflectUtils.getCodeBase(exception.getClass()); 33 if (serviceFile == null || exceptionFile == null || serviceFile.equals(exceptionFile)){ 34 return result; 35 } 36 // 是JDK自帶的異常,直接拋出 37 String className = exception.getClass().getName(); 38 if (className.startsWith("java.") || className.startsWith("javax.")) { 39 return result; 40 } 41 // 是Dubbo自己的異常,直接拋出 42 if (exception instanceof RpcException) { 43 return result; 44 } 45 46 // 不然,包裝成RuntimeException拋給客戶端 47 return new RpcResult(new RuntimeException(StringUtils.toString(exception))); 48 } catch (Throwable e) { 49 logger.warn("Fail to ExceptionFilter when called by " + RpcContext.getContext().getRemoteHost() 50 + ". service: " + invoker.getInterface().getName() + ", method: " + invocation.getMethodName() 51 + ", exception: " + e.getClass().getName() + ": " + e.getMessage(), e); 52 return result; 53 } 54 } 55 return result; 56 } catch (RuntimeException e) { 57 logger.error("Got unchecked and undeclared exception which called by " + RpcContext.getContext().getRemoteHost() 58 + ". service: " + invoker.getInterface().getName() + ", method: " + invocation.getMethodName() 59 + ", exception: " + e.getClass().getName() + ": " + e.getMessage(), e); 60 throw e; 61 } 62 }
1.若是是checked異常,直接拋出.很明顯,咱們的HelloException是RuntimeException,不符合
2.在方法簽名上有聲明,直接拋出.很明顯,咱們接口並未聲明該異常,不符合
3.異常類和接口類在同一jar包裏,直接拋出.很明顯,咱們的異常類是在common.jar的,接口是在api.jar的,不符合
4.是JDK自帶的異常,直接拋出.很明顯,這個HelloException是咱們自定義的,不符合
5.是Dubbo自己的異常(RpcException),直接拋出.很明顯,這個HelloException是咱們自定義的,和RpcException幾乎沒有半毛錢關係.
6.不然,包裝成RuntimeException拋給客戶端.由於以上5點均不知足,因此該異常會被包裝成RuntimeException異常拋出(重要)
這也就是爲何咱們catchHelloException是catch不到的,由於他包裝成RuntimeException了
Dubbo爲何這麼設計
也許你看到這裏會以爲這個判斷好坑.Dubbo爲何要這麼設計?咱們看源碼,最重要的是知道做者爲何這麼設計,只有知道爲何這麼設計纔是通過了深度的思考,不然看時高潮,看後就忘.講清楚爲何這麼設計,也是你們關注肥朝公衆號的一個重要緣由.
其實Dubbo的這個考慮,是基於序列化來考慮的.你想一想,若是provider拋出一個僅在provider自定義的一個異常,那麼該異常到達consumer,明顯是沒法序列化的.因此你注意看Dubbo的判斷.咱們來看下他的判斷
1.若是是checked異常,直接拋出.很明顯,咱們的HelloException是RuntimeException,不符合
2.在方法簽名上有聲明,直接拋出.很明顯,咱們接口並未聲明該異常,不符合
3.異常類和接口類在同一jar包裏,直接拋出.很明顯,咱們的異常類是在common.jar的,接口是在api.jar的,不符合
4.是JDK自帶的異常,直接拋出.很明顯,這個HelloException是咱們自定義的,不符合
5.是Dubbo自己的異常(RpcException),直接拋出.很明顯,這個HelloException是咱們自定義的,和RpcException幾乎沒有半毛錢關係.
6.不然,包裝成RuntimeException拋給客戶端.由於以上5點均不知足,因此該異常會被包裝成RuntimeException異常拋出(重要)
如何解決
既然都知道了原理了,那麼很好解決,我隨便列舉一下,好比從規範上要求業務方接口聲明HelloException
寫在最後
固然肥朝面試的時候,也曾經被問過相似問題,你用XXX有沒有遇到過什麼坑.在一波操做猛如虎的分析下,面試官說
"你真帥".
肥朝會心一笑
結果他卻說
"你笑起來更帥"!
歡迎工做一到八年的Java工程師朋友們加入Java高級交流:854630135
本羣提供免費的學習指導 架構資料 以及免費的解答
不懂得問題均可以在本羣提出來 以後還會有直播平臺和講師直接交流噢