MyBatis源碼剖析之Mapper代理方式詳解
具體代碼如下:
//前三步都相同
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
//這?不再調(diào)?SqlSession的api,?是獲得了接?對象,調(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);思考?個問題,通常的Mapper接?我們都沒有實現(xiàn)的?法卻可以使?,是為什么呢? 答案很簡單:動態(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<?>>();
}開始之前介紹?下MyBatis初始化時對接?的處理:MapperRegistry是Configuration中的?個屬性,它內(nèi)部維護?個HashMap?于存放mapper接?的??類,每個接?對應(yīng)?個??類。mappers中可以配置接?的包路徑,或者某個具體的接?類。
<mappers> <mapper class="com.zjq.mapper.UserMapper"/> <package name="com.zjq.mapper"/> </mappers>
當解析mappers標簽時,它會判斷解析到的是mapper配置?件時,會再將對應(yīng)配置?件中的增刪改查標簽 封裝成MappedStatement對象,存?mappedStatements中。(上?介紹了)當判斷解析到接?時,會建此接?對應(yīng)的MapperProxyFactory對象,存?HashMap中,key =接?的字節(jié)碼對象,value =此接?對應(yīng)MapperProxyFactory對象。
源碼剖析-getmapper()
進? 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通過動態(tài)代理???成實例
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動態(tài)代理的Handler類
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 類,實現(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,說明每個session中的代理對象的不同的!
public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) {
this.sqlSession = sqlSession;
this.mapperInterface = mapperInterface;
this.methodCache = methodCache;
}
//省略部分源碼
}源碼剖析-invoke()
在動態(tài)代理返回了示例后,我們就可以直接調(diào)?mapper類中的?法了,但代理對象調(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 對象
final MapperMethod mapperMethod = cachedMapperMethod(method);
//重點在這:最終調(diào)?了MapperMethod執(zhí)?的?法
return mapperMethod.execute(sqlSession, args);
}
}進?execute?法:
public class MapperMethod {
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
//判斷mapper中的?法類型,最終調(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ù),則將查詢的結(jié)果,提交給 ResultHandler 進?處理
if (method.returnsVoid() && method.hasResultHandler()) {
executeWithResultHandler(sqlSession, args);
result = null;
//執(zhí)?查詢,返回列表
} else if (method.returnsMany()) {
result = executeForMany(sqlSession, args);
//執(zhí)?查詢,返回Map
} else if (method.returnsMap()) {
result = executeForMap(sqlSession, args);
//執(zhí)?查詢,返回Cursor
} else if (method.returnsCursor()) {
result = executeForCursor(sqlSession, args);
//執(zhí)?查詢,返回單個對象
} else {
//轉(zhuǎn)換參數(shù)
Object param = method.convertArgsToSqlCommandParam(args);
//查詢單條
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,并且返回類型為基本類型,則拋出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)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java集合之Comparable和Comparator接口詳解
Java提供了Comparable接口與Comparator接口,它們?yōu)閿?shù)組或集合中的元素提供了排序邏輯,實現(xiàn)此接口的對象數(shù)組或集合可以通過Arrays.sort或Collections.sort進行自動排序。本文將通過示例講講它們的使用,需要的可以參考一下2022-12-12
Java Socket通信(一)之客戶端程序 發(fā)送和接收數(shù)據(jù)
對于Socket通信簡述,服務(wù)端往Socket的輸出流里面寫東西,客戶端就可以通過Socket的輸入流讀取對應(yīng)的內(nèi)容,Socket與Socket之間是雙向連通的,所以客戶端也可以往對應(yīng)的Socket輸出流里面寫東西,然后服務(wù)端對應(yīng)的Socket的輸入流就可以讀出對應(yīng)的內(nèi)容2016-03-03
java實現(xiàn)html轉(zhuǎn)pdf方法步驟
這篇文章主要給大家介紹了關(guān)于java實現(xiàn)html轉(zhuǎn)pdf方法的相關(guān)資料,要將HTML轉(zhuǎn)換成PDF,我們需要借助Java中的第三方庫,文中通過示例代碼介紹的非常詳細,需要的朋友可以參考下2023-08-08
如何在Java中調(diào)用python文件執(zhí)行詳解
豐富的第三方庫使得python非常適合用于進行數(shù)據(jù)分析,最近在項目中就涉及到j(luò)ava調(diào)用python實現(xiàn)的算法,下面這篇文章主要給大家介紹了關(guān)于如何在Java中調(diào)用python文件執(zhí)行的相關(guān)資料,需要的朋友可以參考下2022-05-05
java8升級到j(luò)ava17的兼容性分析與遷移指南
這篇文章主要為大家詳細介紹了從?Java?8?升級到?Java?17?的詳細分析和遷移步驟,包括代碼修改建議,依賴更新和配置調(diào)整,有需要的小伙伴可以參考一下2025-04-04
java Quartz定時器任務(wù)與Spring task定時的幾種實現(xiàn)方法
本篇文章主要介紹了java Quartz定時器任務(wù)與Spring task定時的幾種實現(xiàn)方法的相關(guān)資料,具有一定的參考價值,感興趣的小伙伴們可以參考一下。2017-02-02

