Mybatis懶加載的實現(xiàn)
因為通過javassist和cglib代理實現(xiàn)的,所以說到底最主要的就是JavasisstProxyFactory類中的invoke方法和里面的load方法。
其實讀一讀,里面的邏輯就是跟配置中定義的規(guī)則一樣的
因為github上的mybatis中文版中這部分注釋比較少,所以從網(wǎng)上尋找博客,截取了代碼注釋片段記錄下。
JavasisstProxyFactory
public class JavassistProxyFactory implements org.apache.ibatis.executor.loader.ProxyFactory {
/**
* 接口實現(xiàn)
* @param target 目標(biāo)結(jié)果對象
* @param lazyLoader 延遲加載對象
* @param configuration 配置
* @param objectFactory 對象工廠
* @param constructorArgTypes 構(gòu)造參數(shù)類型
* @param constructorArgs 構(gòu)造參數(shù)值
* @return
*/
@Override
public Object createProxy(Object target, ResultLoaderMap lazyLoader, Configuration configuration, ObjectFactory objectFactory, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
return EnhancedResultObjectProxyImpl.createProxy(target, lazyLoader, configuration, objectFactory, constructorArgTypes, constructorArgs);
}
//省略...
/**
* 代理對象實現(xiàn),核心邏輯執(zhí)行
*/
private static class EnhancedResultObjectProxyImpl implements MethodHandler {
/**
* 創(chuàng)建代理對象
* @param type
* @param callback
* @param constructorArgTypes
* @param constructorArgs
* @return
*/
static Object crateProxy(Class<?> type, MethodHandler callback, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
ProxyFactory enhancer = new ProxyFactory();
enhancer.setSuperclass(type);
try {
//通過獲取對象方法,判斷是否存在該方法
type.getDeclaredMethod(WRITE_REPLACE_METHOD);
// ObjectOutputStream will call writeReplace of objects returned by writeReplace
if (log.isDebugEnabled()) {
log.debug(WRITE_REPLACE_METHOD + " method was found on bean " + type + ", make sure it returns this");
}
} catch (NoSuchMethodException e) {
//沒找到該方法,實現(xiàn)接口
enhancer.setInterfaces(new Class[]{WriteReplaceInterface.class});
} catch (SecurityException e) {
// nothing to do here
}
Object enhanced;
Class<?>[] typesArray = constructorArgTypes.toArray(new Class[constructorArgTypes.size()]);
Object[] valuesArray = constructorArgs.toArray(new Object[constructorArgs.size()]);
try {
//創(chuàng)建新的代理對象
enhanced = enhancer.create(typesArray, valuesArray);
} catch (Exception e) {
throw new ExecutorException("Error creating lazy proxy. Cause: " + e, e);
}
//設(shè)置代理執(zhí)行器
((Proxy) enhanced).setHandler(callback);
return enhanced;
}
/**
* 代理對象執(zhí)行
* @param enhanced 原對象
* @param method 原對象方法
* @param methodProxy 代理方法
* @param args 方法參數(shù)
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object enhanced, Method method, Method methodProxy, Object[] args) throws Throwable {
final String methodName = method.getName();
try {
synchronized (lazyLoader) {
if (WRITE_REPLACE_METHOD.equals(methodName)) {
//忽略暫未找到具體作用
Object original;
if (constructorArgTypes.isEmpty()) {
original = objectFactory.create(type);
} else {
original = objectFactory.create(type, constructorArgTypes, constructorArgs);
}
PropertyCopier.copyBeanProperties(type, enhanced, original);
if (lazyLoader.size() > 0) {
return new JavassistSerialStateHolder(original, lazyLoader.getProperties(), objectFactory, constructorArgTypes, constructorArgs);
} else {
return original;
}
} else {
//延遲加載數(shù)量大于0
if (lazyLoader.size() > 0 && !FINALIZE_METHOD.equals(methodName)) {
//aggressive 一次加載性所有需要要延遲加載屬性或者包含觸發(fā)延遲加載方法
if (aggressive || lazyLoadTriggerMethods.contains(methodName)) {
log.debug("==> laze lod trigger method:" + methodName + ",proxy method:" + methodProxy.getName() + " class:" + enhanced.getClass());
//一次全部加載
lazyLoader.loadAll();
} else if (PropertyNamer.isSetter(methodName)) {
//判斷是否為set方法,set方法不需要延遲加載
final String property = PropertyNamer.methodToProperty(methodName);
lazyLoader.remove(property);
} else if (PropertyNamer.isGetter(methodName)) {
final String property = PropertyNamer.methodToProperty(methodName);
if (lazyLoader.hasLoader(property)) {
//延遲加載單個屬性
lazyLoader.load(property);
log.debug("load one :" + methodName);
}
}
}
}
}
return methodProxy.invoke(enhanced, args);
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
}
load方法
/**
* 執(zhí)行懶加載查詢,獲取數(shù)據(jù)并且set到userObject中返回
* @param userObject
* @throws SQLException
*/
public void load(final Object userObject) throws SQLException {
//合法性校驗
if (this.metaResultObject == null || this.resultLoader == null) {
if (this.mappedParameter == null) {
throw new ExecutorException("Property [" + this.property + "] cannot be loaded because "
+ "required parameter of mapped statement ["
+ this.mappedStatement + "] is not serializable.");
}
//獲取mappedstatement并且校驗
final Configuration config = this.getConfiguration();
final MappedStatement ms = config.getMappedStatement(this.mappedStatement);
if (ms == null) {
throw new ExecutorException("Cannot lazy load property [" + this.property
+ "] of deserialized object [" + userObject.getClass()
+ "] because configuration does not contain statement ["
+ this.mappedStatement + "]");
}
//使用userObject構(gòu)建metaobject,并且重新構(gòu)建resultloader對象
this.metaResultObject = config.newMetaObject(userObject);
this.resultLoader = new ResultLoader(config, new ClosedExecutor(), ms, this.mappedParameter,
metaResultObject.getSetterType(this.property), null, null);
}
/* We are using a new executor because we may be (and likely are) on a new thread
* and executors aren't thread safe. (Is this sufficient?)
*
* A better approach would be making executors thread safe. */
if (this.serializationCheck == null) {
final ResultLoader old = this.resultLoader;
this.resultLoader = new ResultLoader(old.configuration, new ClosedExecutor(), old.mappedStatement,
old.parameterObject, old.targetType, old.cacheKey, old.boundSql);
}
//獲取數(shù)據(jù)庫查詢結(jié)果并且set到結(jié)果對象返回
this.metaResultObject.setValue(property, this.resultLoader.loadResult());
}
參考地址:
https://www.cnblogs.com/qixidi/p/10251126.html
https://blog.csdn.net/mingtian625/article/details/47358003
到此這篇關(guān)于Mybatis懶加載的實現(xiàn)的文章就介紹到這了,更多相關(guān)Mybatis 懶加載內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
詳解spring cloud如何使用spring-test進(jìn)行單元測試
這篇文章主要介紹了spring cloud如何使用spring-test進(jìn)行單元測試,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-11-11
Java實現(xiàn)md5和base64加密解密的示例代碼
這篇文章主要介紹了Java實現(xiàn)md5和base64加密解密的示例代碼,幫助大家更好的利用Java加密解密文件,感興趣的朋友可以了解下2020-09-09
logback FixedWindowRollingPolicy固定窗口算法重命名文件滾動策略
這篇文章主要介紹了FixedWindowRollingPolicy根據(jù)logback 固定窗口算法重命名文件滾動策略源碼解讀,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-11-11
Java?easyExcel的復(fù)雜表頭多級表頭導(dǎo)入
最近在項目開發(fā)中遇到的一個excel復(fù)雜表頭的導(dǎo)入數(shù)據(jù)庫操作,下面這篇文章主要給大家介紹了關(guān)于Java?easyExcel的復(fù)雜表頭多級表頭導(dǎo)入的相關(guān)資料,需要的朋友可以參考下2022-06-06

