一、背景
最近在工作之余,把mybatis的源碼看了下,決定自己手寫個簡單版的。實現核心的功能即可。寫完之后,執行了一下,正巧在mybatis對Mapper接口的動態代理這個核心代碼這邊發現一個問題。正好再回頭看下jdk的動態代理,才發現問題所在。
二、問題
當我用SqlSession.getMapper() 方法來獲取Mapper的代理類的時候,發現這個代理對象所展示的toString()是個null。如下圖
而debug了一下mybatis的源碼,發現是有值,並且的確是new 代理的目標對象為MapperProxy類型
三、回頭看jdk動態代理
當我在排查問題的時候,無意中發現,在執行(T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);方法時,竟然會拋出 mapperProxy類的invoke方法中的異常,但是這個時候程序還沒有走到任何代理的方法。
回頭找了下之前學着寫的動態代理的例子,並在invoke方法里面輸出了method。如下:
啟動main方法,發現打印的內容:
這時候才發現,在實例化代理類的時候,會調用一次invoke()方法,並且此時調用方法的method參數是Object.toString() 。看到這發現了兩點
- invoke方法在實例化代理的時候會調用一次,如果這個方法有對全局的公共變量做修改的話,會存在隱患的問題
- 代理類的toString 最后出來的類的名稱就是調用這個方法得到的。將代碼中result輸出就是對應的代理類。
四、發現並解決問題
回到自己的手寫的mybatis源碼中,自然就定位到了如下這個地方:
對比一下mybatis的源碼
正是紅框中的代碼,自己手寫的時候認為代理的方法的聲明類都是定義的mapper接口,這邊的判斷其實沒有必要,也走不到。然而從第三點中可以知道,在new 代理類的時候傳入的Method為toString方法正好進了這個分支,並且調用了當前MapperProxy實例的toString方法,返回了代理類的名稱,正是之前所缺失的。把這段代碼加上就OK了