MyBatis源碼剖析之Mapper代理方式詳解
具體代碼如下:
//前三步都相同
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
//這?不再調(diào)?SqlSession的api,?是獲得了接?對(duì)象,調(diào)?接?中的?法。
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
//模擬ids的數(shù)據(jù)
List<Integer> ids = new ArrayList<Integer>();
ids.add(1);
ids.add(2);
List<User> userList = mapper.findByIds(ids);思考?個(gè)問(wèn)題,通常的Mapper接?我們都沒(méi)有實(shí)現(xiàn)的?法卻可以使?,是為什么呢? 答案很簡(jiǎn)單:動(dòng)態(tài)代理
public class Configuration {
protected final MapperRegistry mapperRegistry = new MapperRegistry(this);
}
public class MapperRegistry {
private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<Class<?>, MapperProxyFactory<?>>();
}開(kāi)始之前介紹?下MyBatis初始化時(shí)對(duì)接?的處理:MapperRegistry是Configuration中的?個(gè)屬性,它內(nèi)部維護(hù)?個(gè)HashMap?于存放mapper接?的??類(lèi),每個(gè)接?對(duì)應(yīng)?個(gè)??類(lèi)。mappers中可以配置接?的包路徑,或者某個(gè)具體的接?類(lèi)。
<mappers> <mapper class="com.zjq.mapper.UserMapper"/> <package name="com.zjq.mapper"/> </mappers>
當(dāng)解析mappers標(biāo)簽時(shí),它會(huì)判斷解析到的是mapper配置?件時(shí),會(huì)再將對(duì)應(yīng)配置?件中的增刪改查標(biāo)簽 封裝成MappedStatement對(duì)象,存?mappedStatements中。(上?介紹了)當(dāng)判斷解析到接?時(shí),會(huì)建此接?對(duì)應(yīng)的MapperProxyFactory對(duì)象,存?HashMap中,key =接?的字節(jié)碼對(duì)象,value =此接?對(duì)應(yīng)MapperProxyFactory對(duì)象。
源碼剖析-getmapper()
進(jìn)? sqlSession.getMapper(UserMapper.class )中
public interface SqlSession extends Closeable {
//調(diào)用configuration中的getMapper
<T> T getMapper(Class<T> type);
}
public class DefaultSqlSession implements SqlSession {
@Override
public <T> T getMapper(Class<T> type) {
//調(diào)用configuration 中的getMapper
return configuration.<T>getMapper(type, this);
}
}
public class Configuration {
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
//調(diào)用MapperRegistry 中的getMapper
return mapperRegistry.getMapper(type, sqlSession);
}
}
public class MapperRegistry {
@SuppressWarnings("unchecked")
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
//從 MapperRegistry 中的 HashMap 中拿 MapperProxyFactory
final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
}
try {
//調(diào)用MapperProxyFactory的newInstance通過(guò)動(dòng)態(tài)代理???成實(shí)例
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}
}
public class MapperProxyFactory<T> {
public T newInstance(SqlSession sqlSession) {
//創(chuàng)建了 JDK動(dòng)態(tài)代理的Handler類(lèi)
final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
//調(diào)?了重載?法
return newInstance(mapperProxy);
}
@SuppressWarnings("unchecked")
protected T newInstance(MapperProxy<T> mapperProxy) {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
}
//MapperProxy 類(lèi),實(shí)現(xiàn)了 InvocationHandler 接?
public class MapperProxy<T> implements InvocationHandler, Serializable {
//省略部分源碼
private final SqlSession sqlSession;
private final Class<T> mapperInterface;
private final Map<Method, MapperMethod> methodCache;
//構(gòu)造,傳?了 SqlSession,說(shuō)明每個(gè)session中的代理對(duì)象的不同的!
public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) {
this.sqlSession = sqlSession;
this.mapperInterface = mapperInterface;
this.methodCache = methodCache;
}
//省略部分源碼
}源碼剖析-invoke()
在動(dòng)態(tài)代理返回了示例后,我們就可以直接調(diào)?mapper類(lèi)中的?法了,但代理對(duì)象調(diào)??法,執(zhí)?是在MapperProxy中的invoke?法中。
public class MapperProxy<T> implements InvocationHandler, Serializable {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
//如果是Object定義的?法,直接調(diào)?
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
} else if (isDefaultMethod(method)) {
return invokeDefaultMethod(proxy, method, args);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
// 獲得 MapperMethod 對(duì)象
final MapperMethod mapperMethod = cachedMapperMethod(method);
//重點(diǎn)在這:最終調(diào)?了MapperMethod執(zhí)?的?法
return mapperMethod.execute(sqlSession, args);
}
}進(jìn)?execute?法:
public class MapperMethod {
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
//判斷mapper中的?法類(lèi)型,最終調(diào)?的還是SqlSession中的?法 switch(command.getType())
switch (command.getType()) {
case INSERT: {
//轉(zhuǎn)換參數(shù)
Object param = method.convertArgsToSqlCommandParam(args);
//執(zhí)?INSERT操作
//轉(zhuǎn)換rowCount
result = rowCountResult(sqlSession.insert(command.getName(), param));
break;
}
case UPDATE: {
//轉(zhuǎn)換參數(shù)
Object param = method.convertArgsToSqlCommandParam(args);
// 轉(zhuǎn)換 rowCount
result = rowCountResult(sqlSession.update(command.getName(), param));
break;
}
case DELETE: {
//轉(zhuǎn)換參數(shù)
Object param = method.convertArgsToSqlCommandParam(args);
// 轉(zhuǎn)換 rowCount
result = rowCountResult(sqlSession.delete(command.getName(), param));
break;
}
case SELECT:
//?返回,并且有ResultHandler?法參數(shù),則將查詢(xún)的結(jié)果,提交給 ResultHandler 進(jìn)?處理
if (method.returnsVoid() && method.hasResultHandler()) {
executeWithResultHandler(sqlSession, args);
result = null;
//執(zhí)?查詢(xún),返回列表
} else if (method.returnsMany()) {
result = executeForMany(sqlSession, args);
//執(zhí)?查詢(xún),返回Map
} else if (method.returnsMap()) {
result = executeForMap(sqlSession, args);
//執(zhí)?查詢(xún),返回Cursor
} else if (method.returnsCursor()) {
result = executeForCursor(sqlSession, args);
//執(zhí)?查詢(xún),返回單個(gè)對(duì)象
} else {
//轉(zhuǎn)換參數(shù)
Object param = method.convertArgsToSqlCommandParam(args);
//查詢(xún)單條
result = sqlSession.selectOne(command.getName(), param);
}
break;
case FLUSH:
result = sqlSession.flushStatements();
break;
default:
throw new BindingException("Unknown execution method for: " + command.getName());
}
//返回結(jié)果為null,并且返回類(lèi)型為基本類(lèi)型,則拋出BindingException異常
if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
throw new BindingException("Mapper method '" + command.getName()
+ " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
}
//返回結(jié)果
return result;
}
}到此這篇關(guān)于MyBatis源碼剖析之Mapper代理方式詳解的文章就介紹到這了,更多相關(guān)MyBatis Mapper代理方式內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java集合之Comparable和Comparator接口詳解
Java提供了Comparable接口與Comparator接口,它們?yōu)閿?shù)組或集合中的元素提供了排序邏輯,實(shí)現(xiàn)此接口的對(duì)象數(shù)組或集合可以通過(guò)Arrays.sort或Collections.sort進(jìn)行自動(dòng)排序。本文將通過(guò)示例講講它們的使用,需要的可以參考一下2022-12-12
Java Socket通信(一)之客戶(hù)端程序 發(fā)送和接收數(shù)據(jù)
對(duì)于Socket通信簡(jiǎn)述,服務(wù)端往Socket的輸出流里面寫(xiě)東西,客戶(hù)端就可以通過(guò)Socket的輸入流讀取對(duì)應(yīng)的內(nèi)容,Socket與Socket之間是雙向連通的,所以客戶(hù)端也可以往對(duì)應(yīng)的Socket輸出流里面寫(xiě)東西,然后服務(wù)端對(duì)應(yīng)的Socket的輸入流就可以讀出對(duì)應(yīng)的內(nèi)容2016-03-03
java實(shí)現(xiàn)html轉(zhuǎn)pdf方法步驟
這篇文章主要給大家介紹了關(guān)于java實(shí)現(xiàn)html轉(zhuǎn)pdf方法的相關(guān)資料,要將HTML轉(zhuǎn)換成PDF,我們需要借助Java中的第三方庫(kù),文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-08-08
如何在Java中調(diào)用python文件執(zhí)行詳解
豐富的第三方庫(kù)使得python非常適合用于進(jìn)行數(shù)據(jù)分析,最近在項(xiàng)目中就涉及到j(luò)ava調(diào)用python實(shí)現(xiàn)的算法,下面這篇文章主要給大家介紹了關(guān)于如何在Java中調(diào)用python文件執(zhí)行的相關(guān)資料,需要的朋友可以參考下2022-05-05
java8升級(jí)到j(luò)ava17的兼容性分析與遷移指南
這篇文章主要為大家詳細(xì)介紹了從?Java?8?升級(jí)到?Java?17?的詳細(xì)分析和遷移步驟,包括代碼修改建議,依賴(lài)更新和配置調(diào)整,有需要的小伙伴可以參考一下2025-04-04
java Quartz定時(shí)器任務(wù)與Spring task定時(shí)的幾種實(shí)現(xiàn)方法
本篇文章主要介紹了java Quartz定時(shí)器任務(wù)與Spring task定時(shí)的幾種實(shí)現(xiàn)方法的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下。2017-02-02

