1. 程式人生 > 實用技巧 >Mybatis介面Mapper內的方法為啥不能過載嗎?

Mybatis介面Mapper內的方法為啥不能過載嗎?

動態代理的功能:通過攔截器方法回撥,對目標target方法進行增強。

言外之意就是為了增強目標target方法。上面這句話沒錯,但也不要認為它就是真理,殊不知,動態代理還有投鞭斷流的霸權,連目標target都不要的科幻模式。

注:本文預設認為,讀者對動態代理的原理是理解的,如果不明白target的含義,難以看懂本篇文章,建議先理解動態代理。

1. 自定義JDK動態代理之投鞭斷流實現自動對映器Mapper

首先定義一個pojo。

publicclassUser{
privateIntegerid;
privateStringname;
privateintage;

publicUser(Integerid,Stringname,intage){
this.id=id;
this.name=name;
this.age=age;
}
//gettersetter
}

再定義一個介面UserMapper.java。

publicinterfaceUserMapper{
publicUsergetUserById(Integerid);
}

接下來我們看看如何使用動態代理之投鞭斷流,實現例項化介面並呼叫介面方法返回資料的。

自定義一個InvocationHandler。

importjava.lang.reflect.InvocationHandler;
importjava.lang.reflect.Method;
importjava.lang.reflect.Proxy;

publicclassMapperProxyimplementsInvocationHandler{

@SuppressWarnings("unchecked")
public<T>TnewInstance(Class<T>clz){
return(T)Proxy.newProxyInstance(clz.getClassLoader(),newClass[]{clz},this);
}

@Override
publicObjectinvoke(Objectproxy,Methodmethod,Object[]args)throwsThrowable{
if(Object.class.equals(method.getDeclaringClass())){
try{
//諸如hashCode()、toString()、equals()等方法,將target指向當前物件this
returnmethod.invoke(this,args);
}catch(Throwablet){
}
}
//投鞭斷流
returnnewUser((Integer)args[0],"zhangsan",18);
}
}

上面程式碼中的target,在執行Object.java內的方法時,target被指向了this,target已經變成了傀儡、象徵、佔位符。在投鞭斷流式的攔截時,已經沒有了target。

寫一個測試程式碼:


publicstaticvoidmain(String[]args){
MapperProxyproxy=newMapperProxy();

UserMappermapper=proxy.newInstance(UserMapper.class);
Useruser=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的底層實現原理。

可能有讀者不禁要問:你怎麼把程式碼寫的像初學者寫的一樣?沒有結構,且缺乏美感。

必須宣告,作為一名經驗老道的高手,能把程式寫的像初學者寫的一樣,那必定是高手中的高手。這樣可以讓初學者感覺到親切,舒服,符合自己的Style,讓他們或她們,感覺到大牛寫的程式碼也不過如此,自己甚至寫的比這些大牛寫的還要好,從此自信滿滿,熱情高漲,認為與大牛之間的差距,僅剩下三分鐘。

2. Mybatis自動對映器Mapper的原始碼分析

首先編寫一個測試類:

publicstaticvoidmain(String[]args){
SqlSessionsqlSession=MybatisSqlSessionFactory.openSession();
try{
StudentMapperstudentMapper=sqlSession.getMapper(StudentMapper.class);
List<Student>students=studentMapper.findAllStudents();
for(Studentstudent:students){
System.out.println(student);
}
}finally{
sqlSession.close();
}
}

Mapper長這個樣子:


publicinterfaceStudentMapper{
List<Student>findAllStudents();
StudentfindStudentById(Integerid);
voidinsertStudent(Studentstudent);
}

org.apache.ibatis.binding.MapperProxy.java部分原始碼。

publicclassMapperProxy<T>implementsInvocationHandler,Serializable{

privatestaticfinallongserialVersionUID=-6424540398559729838L;
privatefinalSqlSessionsqlSession;
privatefinalClass<T>mapperInterface;
privatefinalMap<Method,MapperMethod>methodCache;

publicMapperProxy(SqlSessionsqlSession,Class<T>mapperInterface,Map<Method,MapperMethod>methodCache){
this.sqlSession=sqlSession;
this.mapperInterface=mapperInterface;
this.methodCache=methodCache;
}

@Override
publicObjectinvoke(Objectproxy,Methodmethod,Object[]args)throwsThrowable{
if(Object.class.equals(method.getDeclaringClass())){
try{
returnmethod.invoke(this,args);
}catch(Throwablet){
throwExceptionUtil.unwrapThrowable(t);
}
}
//投鞭斷流
finalMapperMethodmapperMethod=cachedMapperMethod(method);
returnmapperMethod.execute(sqlSession,args);
}
//...

org.apache.ibatis.binding.MapperProxyFactory.java部分原始碼。

publicclassMapperProxyFactory<T>{

privatefinalClass<T>mapperInterface;

@SuppressWarnings("unchecked")
protectedTnewInstance(MapperProxy<T>mapperProxy){
return(T)Proxy.newProxyInstance(mapperInterface.getClassLoader(),newClass[]{mapperInterface},mapperProxy);
}

這便是Mybatis使用動態代理之投鞭斷流。

3. 介面Mapper內的方法能過載(overLoad)嗎?(重要)

類似下面:

publicUsergetUserById(Integerid);
publicUsergetUserById(Integerid,Stringname);

Answer:不能。

原因:在投鞭斷流時,Mybatis使用package+Mapper+method全限名作為key,去xml內尋找唯一sql來執行的。

類似:key=x.y.UserMapper.getUserById,那麼,過載方法時將導致矛盾。對於Mapper介面,Mybatis禁止方法過載(overLoad)。