Mybatis接口Mapper內(nèi)的方法為啥不能重載?
來(lái)源:https://my.oschina.net/zudajun/blog/666223
動(dòng)態(tài)代理的功能:通過(guò)攔截器方法回調(diào),對(duì)目標(biāo)target方法進(jìn)行增強(qiáng)。
言外之意就是為了增強(qiáng)目標(biāo)target方法。上面這句話沒(méi)錯(cuò),但也不要認(rèn)為它就是真理,殊不知,動(dòng)態(tài)代理還有投鞭斷流的霸權(quán),連目標(biāo)target都不要的科幻模式。
注:本文默認(rèn)認(rèn)為,讀者對(duì)動(dòng)態(tài)代理的原理是理解的,如果不明白target的含義,難以看懂本篇文章,建議先理解動(dòng)態(tài)代理。
1. 自定義JDK動(dòng)態(tài)代理之投鞭斷流實(shí)現(xiàn)自動(dòng)映射器Mapper
首先定義一個(gè)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
}
再定義一個(gè)接口UserMapper.java。
public?interface?UserMapper?{
?public?User?getUserById(Integer?id);?
}
接下來(lái)我們看看如何使用動(dòng)態(tài)代理之投鞭斷流,實(shí)現(xiàn)實(shí)例化接口并調(diào)用接口方法返回?cái)?shù)據(jù)的。
自定義一個(gè)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?newInstance(Class?clz) ?{
??return?(T)?Proxy.newProxyInstance(clz.getClassLoader(),?new?Class[]?{?clz?},?this);
?}
?@Override
?public?Object?invoke(Object?proxy,?Method?method,?Object[]?args)?throws?Throwable?{
??if?(Object.class.equals(method.getDeclaringClass()))?{
???try?{
????//?諸如hashCode()、toString()、equals()等方法,將target指向當(dāng)前對(duì)象this
????return?method.invoke(this,?args);
???}?catch?(Throwable?t)?{
???}
??}
??//?投鞭斷流
??return?new?User((Integer)?args[0],?"zhangsan",?18);
?}
}
上面代碼中的target,在執(zhí)行Object.java內(nèi)的方法時(shí),target被指向了this,target已經(jīng)變成了傀儡、象征、占位符。在投鞭斷流式的攔截時(shí),已經(jīng)沒(méi)有了target。
寫(xiě)一個(gè)測(cè)試代碼:
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自動(dòng)映射器Mapper的底層實(shí)現(xiàn)原理。
可能有讀者不禁要問(wèn):你怎么把代碼寫(xiě)的像初學(xué)者寫(xiě)的一樣?沒(méi)有結(jié)構(gòu),且缺乏美感。
必須聲明,作為一名經(jīng)驗(yàn)老道的高手,能把程序?qū)懙南癯鯇W(xué)者寫(xiě)的一樣,那必定是高手中的高手。這樣可以讓初學(xué)者感覺(jué)到親切,舒服,符合自己的Style,讓他們或她們,感覺(jué)到大牛寫(xiě)的代碼也不過(guò)如此,自己甚至寫(xiě)的比這些大牛寫(xiě)的還要好,從此自信滿滿,熱情高漲,認(rèn)為與大牛之間的差距,僅剩下三分鐘。
2. Mybatis自動(dòng)映射器Mapper的源碼分析
首先編寫(xiě)一個(gè)測(cè)試類:
public?static?void?main(String[]?args)?{
SqlSession?sqlSession?=?MybatisSqlSessionFactory.openSession();
try?{
??StudentMapper?studentMapper?=?sqlSession.getMapper(StudentMapper.class);
??List?students?=?studentMapper.findAllStudents();
??for?(Student?student?:?students)?{
????System.out.println(student);
??}
}?finally?{
??sqlSession.close();
}
}
Mapper長(zhǎng)這個(gè)樣子:
public?interface?StudentMapper?{
?List?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?mapperInterface;
??private?final?Map?methodCache;
??public?MapperProxy(SqlSession?sqlSession,?Class?mapperInterface,?Map?methodCache) ?{
????this.sqlSession?=?sqlSession;
????this.mapperInterface?=?mapperInterface;
????this.methodCache?=?methodCache;
??}
??@Override
??public?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?mapperInterface;
??@SuppressWarnings("unchecked")
??protected?T?newInstance(MapperProxy?mapperProxy) ?{
????return?(T)?Proxy.newProxyInstance(mapperInterface.getClassLoader(),?new?Class[]?{?mapperInterface?},?mapperProxy);
??}
這便是Mybatis使用動(dòng)態(tài)代理之投鞭斷流。
3. 接口Mapper內(nèi)的方法能重載(overLoad)嗎?(重要)
類似下面:
public?User?getUserById(Integer?id);
public?User?getUserById(Integer?id,?String?name);
Answer:不能。
原因:在投鞭斷流時(shí),Mybatis使用package+Mapper+method全限名作為key,去xml內(nèi)尋找唯一sql來(lái)執(zhí)行的。類似:key=x.y.UserMapper.getUserById,那么,重載方法時(shí)將導(dǎo)致矛盾。對(duì)于Mapper接口,Mybatis禁止方法重載(overLoad)。
