支付宝二面:Mybatis 接口 Mapper 内的方法为啥不能重载吗?我直接懵逼了。。。
動態(tài)代理的功能:通過攔截器方法回調(diào),對目標target方法進行增強。
言外之意就是為了增強目標target方法。上面這句話沒錯,但也不要認為它就是真理,殊不知,動態(tài)代理還有投鞭斷流的霸權(quán),連目標target都不要的科幻模式。
注:本文默認認為,讀者對動態(tài)代理的原理是理解的,如果不明白target的含義,難以看懂本篇文章,建議先理解動態(tài)代理。
1. 自定義JDK動態(tài)代理之投鞭斷流實現(xiàn)自動映射器Mapper
首先定義一個pojo。
public?class?User?{private?Integer?id;private?String?name;private?int?age;public?User(Integer?id,?String?name,?int?age)?{this.id?=?id;this.name?=?name;this.age?=?age;}//?getter?setter }再定義一個接口UserMapper.java。
public?interface?UserMapper?{public?User?getUserById(Integer?id); }接下來我們看看如何使用動態(tài)代理之投鞭斷流,實現(xiàn)實例化接口并調(diào)用接口方法返回數(shù)據(jù)的。
自定義一個InvocationHandler。
import?java.lang.reflect.InvocationHandler; import?java.lang.reflect.Method; import?java.lang.reflect.Proxy;public?class?MapperProxy?implements?InvocationHandler?{@SuppressWarnings("unchecked")public?<T>?T?newInstance(Class<T>?clz)?{return?(T)?Proxy.newProxyInstance(clz.getClassLoader(),?new?Class[]?{?clz?},?this);}@Overridepublic?Object?invoke(Object?proxy,?Method?method,?Object[]?args)?throws?Throwable?{if?(Object.class.equals(method.getDeclaringClass()))?{try?{//?諸如hashCode()、toString()、equals()等方法,將target指向當前對象thisreturn?method.invoke(this,?args);}?catch?(Throwable?t)?{}}//?投鞭斷流return?new?User((Integer)?args[0],?"zhangsan",?18);} }上面代碼中的target,在執(zhí)行Object.java內(nèi)的方法時,target被指向了this,target已經(jīng)變成了傀儡、象征、占位符。在投鞭斷流式的攔截時,已經(jīng)沒有了target。
寫一個測試代碼:
public?static?void?main(String[]?args)?{MapperProxy?proxy?=?new?MapperProxy();UserMapper?mapper?=?proxy.newInstance(UserMapper.class);User?user?=?mapper.getUserById(1001);System.out.println("ID:"?+?user.getId());System.out.println("Name:"?+?user.getName());System.out.println("Age:"?+?user.getAge());System.out.println(mapper.toString()); }output:
ID:1001 Name:zhangsan Age:18 x.y.MapperProxy@6bc7c054這便是Mybatis自動映射器Mapper的底層實現(xiàn)原理。
可能有讀者不禁要問:你怎么把代碼寫的像初學者寫的一樣?沒有結(jié)構(gòu),且缺乏美感。
必須聲明,作為一名經(jīng)驗老道的高手,能把程序?qū)懙南癯鯇W者寫的一樣,那必定是高手中的高手。這樣可以讓初學者感覺到親切,舒服,符合自己的Style,讓他們或她們,感覺到大牛寫的代碼也不過如此,自己甚至寫的比這些大牛寫的還要好,從此自信滿滿,熱情高漲,認為與大牛之間的差距,僅剩下三分鐘。
“推薦一個艿艿寫的 6000+ Star 的 SpringBoot + SpringCloud + Dubbo 教程的倉庫:https://github.com/YunaiV/SpringBoot-Labs
“推薦一個艿艿寫的 6000+ Star 的 SpringBoot + SpringCloud + Dubbo 教程的倉庫:https://github.com/YunaiV/SpringBoot-Labs
2. Mybatis自動映射器Mapper的源碼分析
首先編寫一個測試類:
public?static?void?main(String[]?args)?{SqlSession?sqlSession?=?MybatisSqlSessionFactory.openSession();try?{StudentMapper?studentMapper?=?sqlSession.getMapper(StudentMapper.class);List<Student>?students?=?studentMapper.findAllStudents();for?(Student?student?:?students)?{System.out.println(student);}}?finally?{sqlSession.close();}}Mapper長這個樣子:
public?interface?StudentMapper?{List<Student>?findAllStudents();Student?findStudentById(Integer?id);void?insertStudent(Student?student); }org.apache.ibatis.binding.MapperProxy.java部分源碼。
public?class?MapperProxy<T>?implements?InvocationHandler,?Serializable?{private?static?final?long?serialVersionUID?=?-6424540398559729838L;private?final?SqlSession?sqlSession;private?final?Class<T>?mapperInterface;private?final?Map<Method,?MapperMethod>?methodCache;public?MapperProxy(SqlSession?sqlSession,?Class<T>?mapperInterface,?Map<Method,?MapperMethod>?methodCache)?{this.sqlSession?=?sqlSession;this.mapperInterface?=?mapperInterface;this.methodCache?=?methodCache;}@Overridepublic?Object?invoke(Object?proxy,?Method?method,?Object[]?args)?throws?Throwable?{if?(Object.class.equals(method.getDeclaringClass()))?{try?{return?method.invoke(this,?args);}?catch?(Throwable?t)?{throw?ExceptionUtil.unwrapThrowable(t);}}//?投鞭斷流final?MapperMethod?mapperMethod?=?cachedMapperMethod(method);return?mapperMethod.execute(sqlSession,?args);}//?...org.apache.ibatis.binding.MapperProxyFactory.java部分源碼。
public?class?MapperProxyFactory<T>?{private?final?Class<T>?mapperInterface;@SuppressWarnings("unchecked")protected?T?newInstance(MapperProxy<T>?mapperProxy)?{return?(T)?Proxy.newProxyInstance(mapperInterface.getClassLoader(),?new?Class[]?{?mapperInterface?},?mapperProxy);}這便是Mybatis使用動態(tài)代理之投鞭斷流。
“推薦一個艿艿寫的 3000+ Star 的 SpringCloud Alibaba 電商開源項目的倉庫:https://github.com/YunaiV/onemall
“推薦一個艿艿寫的 3000+ Star 的 SpringCloud Alibaba 電商開源項目的倉庫:https://github.com/YunaiV/onemall
3. 接口Mapper內(nèi)的方法能重載(overLoad)嗎?(重要)
類似下面:
public?User?getUserById(Integer?id); public?User?getUserById(Integer?id,?String?name);Answer:不能。
原因:在投鞭斷流時,Mybatis使用package+Mapper+method全限名作為key,去xml內(nèi)尋找唯一sql來執(zhí)行的。類似:key=x.y.UserMapper.getUserById,那么,重載方法時將導致矛盾。對于Mapper接口,Mybatis禁止方法重載(overLoad)。
注:學習時,是先研究的源碼,看懂了原理。寫博文時,則先闡釋原理,再閱讀的源碼。順序剛好相反,希望讀者不要因此疑惑,以為我強大到未卜先知。
總結(jié)
以上是生活随笔為你收集整理的支付宝二面:Mybatis 接口 Mapper 内的方法为啥不能重载吗?我直接懵逼了。。。的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Spring事务专题(四)Spring中
- 下一篇: 面试必问:常用的加密算法有哪些?